diff options
1616 files changed, 33853 insertions, 18488 deletions
diff --git a/Android.bp b/Android.bp index 1f17932be895..1c16e466703c 100644 --- a/Android.bp +++ b/Android.bp @@ -466,6 +466,7 @@ java_library { "android.hardware.cas-V1.2-java", "android.hardware.contexthub-V1.0-java", "android.hardware.contexthub-V1.1-java", + "android.hardware.contexthub-V1.2-java", "android.hardware.gnss-V1.0-java", "android.hardware.gnss-V2.1-java", "android.hardware.health-V1.0-java-constants", diff --git a/StubLibraries.bp b/StubLibraries.bp index 7c44f16caa9b..9604466a0edd 100644 --- a/StubLibraries.bp +++ b/StubLibraries.bp @@ -122,7 +122,6 @@ stubs_defaults { droidstubs { name: "api-stubs-docs", defaults: ["metalava-full-api-stubs-default"], - removed_dex_api_filename: "removed-dex.txt", arg_files: [ "core/res/AndroidManifest.xml", ], @@ -183,7 +182,6 @@ module_libs = " " + droidstubs { name: "system-api-stubs-docs", defaults: ["metalava-full-api-stubs-default"], - removed_dex_api_filename: "system-removed-dex.txt", arg_files: [ "core/res/AndroidManifest.xml", ], @@ -231,7 +229,7 @@ droidstubs { droidstubs { name: "test-api-stubs-docs", - defaults: ["metalava-full-api-stubs-default"], + defaults: ["metalava-non-updatable-api-stubs-default"], arg_files: [ "core/res/AndroidManifest.xml", ], @@ -424,7 +422,21 @@ java_library_static { java_library_static { name: "android_test_stubs_current", srcs: [ ":test-api-stubs-docs" ], - static_libs: [ "private-stub-annotations-jar" ], + static_libs: [ + // Modules do not have test APIs, but we want to include their SystemApis, like we include + // the SystemApi of framework-non-updatable-sources. + "conscrypt.module.public.api.stubs", + "framework-appsearch.stubs.system", + "framework-graphics.stubs.system", + "framework-media.stubs.system", + "framework-mediaprovider.stubs.system", + "framework-permission.stubs.system", + "framework-sdkextensions.stubs.system", + "framework-statsd.stubs.system", + "framework-tethering.stubs.system", + "framework-wifi.stubs.system", + "private-stub-annotations-jar", + ], defaults: [ "android_defaults_stubs_current", "android_stubs_dists_default", diff --git a/apct-tests/perftests/core/src/android/text/TextUtilsPerfTest.java b/apct-tests/perftests/core/src/android/text/TextUtilsPerfTest.java new file mode 100644 index 000000000000..c62269eb2978 --- /dev/null +++ b/apct-tests/perftests/core/src/android/text/TextUtilsPerfTest.java @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.text; + +import android.perftests.utils.BenchmarkState; +import android.perftests.utils.PerfStatusReporter; + +import androidx.test.filters.LargeTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.function.Supplier; + +@RunWith(AndroidJUnit4.class) +@LargeTest +public class TextUtilsPerfTest { + @Rule + public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter(); + + public static final String TEMPLATE = "Template that combines %s and %d together"; + + public String mVar1 = "example"; + public int mVar2 = 42; + + /** + * Measure overhead of formatting a string via {@link String#format}. + */ + @Test + public void timeFormatUpstream() { + final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + String res = String.format(TEMPLATE, mVar1, mVar2); + } + } + + /** + * Measure overhead of formatting a string via + * {@link TextUtils#formatSimple}. + */ + @Test + public void timeFormatLocal() { + final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + String res = TextUtils.formatSimple(TEMPLATE, mVar1, mVar2); + } + } + + /** + * Measure overhead of formatting a string inline. + */ + @Test + public void timeFormatInline() { + final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + String res = "Template that combines " + mVar1 + " and " + mVar2 + " together"; + } + } + + /** + * Measure overhead of a passing null-check that uses a lambda to + * communicate a custom error message. + */ + @Test + public void timeFormat_Skip_Lambda() { + final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + requireNonNull(this, () -> { + return String.format(TEMPLATE, mVar1, mVar2); + }); + } + } + + /** + * Measure overhead of a passing null-check that uses varargs to communicate + * a custom error message. + */ + @Test + public void timeFormat_Skip_Varargs() { + final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + while (state.keepRunning()) { + requireNonNull(this, TEMPLATE, mVar1, mVar2); + } + } + + private static <T> T requireNonNull(T obj, Supplier<String> messageSupplier) { + return obj; + } + + private static <T> T requireNonNull(T obj, String format, Object... args) { + return obj; + } +} diff --git a/apct-tests/perftests/windowmanager/README.md b/apct-tests/perftests/windowmanager/README.md index 8b5292fd02a5..7a0019a0c23f 100644 --- a/apct-tests/perftests/windowmanager/README.md +++ b/apct-tests/perftests/windowmanager/README.md @@ -4,7 +4,7 @@ To reduce the variance of the test, if `perf-setup.sh` (platform_testing/scripts/perf-setup) is available, it is better to use the following instructions to lock CPU and GPU frequencies. ``` -m perf-setup.sh +m perf-setup PERF_SETUP_PATH=/data/local/tmp/perf-setup.sh adb push $OUT/$PERF_SETUP_PATH $PERF_SETUP_PATH adb shell chmod +x $PERF_SETUP_PATH diff --git a/apct-tests/perftests/windowmanager/src/android/wm/RelayoutPerfTest.java b/apct-tests/perftests/windowmanager/src/android/wm/RelayoutPerfTest.java index ecd149989ce6..1be68f5d53a4 100644 --- a/apct-tests/perftests/windowmanager/src/android/wm/RelayoutPerfTest.java +++ b/apct-tests/perftests/windowmanager/src/android/wm/RelayoutPerfTest.java @@ -135,7 +135,6 @@ public class RelayoutPerfTest extends WindowManagerPerfTestBase final int mHeight; final Point mOutSurfaceSize = new Point(); final SurfaceControl mOutSurfaceControl; - final SurfaceControl mOutBlastSurfaceControl = new SurfaceControl(); final IntSupplier mViewVisibility; @@ -158,7 +157,7 @@ public class RelayoutPerfTest extends WindowManagerPerfTestBase session.relayout(mWindow, mParams, mWidth, mHeight, mViewVisibility.getAsInt(), mFlags, mFrameNumber, mOutFrames, mOutMergedConfiguration, mOutSurfaceControl, mOutInsetsState, mOutControls, - mOutSurfaceSize, mOutBlastSurfaceControl); + mOutSurfaceSize); } } } diff --git a/apct-tests/perftests/windowmanager/src/android/wm/WindowAddRemovePerfTest.java b/apct-tests/perftests/windowmanager/src/android/wm/WindowAddRemovePerfTest.java index b11d74646d67..6f0001dcc0ad 100644 --- a/apct-tests/perftests/windowmanager/src/android/wm/WindowAddRemovePerfTest.java +++ b/apct-tests/perftests/windowmanager/src/android/wm/WindowAddRemovePerfTest.java @@ -84,6 +84,7 @@ public class WindowAddRemovePerfTest extends WindowManagerPerfTestBase private static class TestWindow extends BaseIWindow { final WindowManager.LayoutParams mLayoutParams = new WindowManager.LayoutParams(); + final InsetsState mRequestedVisibility = new InsetsState(); final Rect mOutFrame = new Rect(); final Rect mOutContentInsets = new Rect(); final Rect mOutStableInsets = new Rect(); @@ -108,7 +109,8 @@ public class WindowAddRemovePerfTest extends WindowManagerPerfTestBase long startTime = SystemClock.elapsedRealtimeNanos(); session.addToDisplay(this, mLayoutParams, View.VISIBLE, - Display.DEFAULT_DISPLAY, mOutFrame, mOutContentInsets, mOutStableInsets, + Display.DEFAULT_DISPLAY, mRequestedVisibility, mOutFrame, + mOutContentInsets, mOutStableInsets, mOutDisplayCutout, inputChannel, mOutInsetsState, mOutControls); final long elapsedTimeNsOfAdd = SystemClock.elapsedRealtimeNanos() - startTime; state.addExtraResult("add", elapsedTimeNsOfAdd); diff --git a/apex/Android.bp b/apex/Android.bp index 266e6720c1a1..c5b4901a9b79 100644 --- a/apex/Android.bp +++ b/apex/Android.bp @@ -107,7 +107,7 @@ java_defaults { // Hide impl library and stub sources impl_library_visibility: [ - ":__package__", + ":__pkg__", "//frameworks/base", // For framework-all ], stubs_source_visibility: ["//visibility:private"], diff --git a/apex/appsearch/framework/Android.bp b/apex/appsearch/framework/Android.bp index e10fb074126f..12afde4a0f70 100644 --- a/apex/appsearch/framework/Android.bp +++ b/apex/appsearch/framework/Android.bp @@ -26,10 +26,8 @@ java_sdk_library { srcs: [ ":framework-appsearch-sources" ], sdk_version: "core_platform", // TODO(b/146218515) should be module_current impl_only_libs: ["framework-minus-apex"], // TODO(b/146218515) should be removed - static_libs: ["icing-java-proto-lite"], defaults: ["framework-module-defaults"], permitted_packages: ["android.app.appsearch"], - jarjar_rules: "jarjar-rules.txt", aidl: { include_dirs: ["frameworks/base/core/java"], // TODO(b/146218515) should be removed }, diff --git a/apex/appsearch/framework/jarjar-rules.txt b/apex/appsearch/framework/jarjar-rules.txt deleted file mode 100644 index acf759a58d56..000000000000 --- a/apex/appsearch/framework/jarjar-rules.txt +++ /dev/null @@ -1,2 +0,0 @@ -rule com.google.protobuf.** android.app.appsearch.protobuf.@1 -rule com.google.android.icing.proto.** android.app.appsearch.proto.@1 diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchBatchResult.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchBatchResult.java index dc758253f1a4..98daa66183a3 100644 --- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchBatchResult.java +++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchBatchResult.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 The Android Open Source Project + * Copyright 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,6 +22,8 @@ import android.os.Parcel; import android.os.Parcelable; import android.util.ArrayMap; +import com.android.internal.util.Preconditions; + import java.util.Collections; import java.util.Map; @@ -33,11 +35,11 @@ import java.util.Map; * @param <ValueType> The type of result objects associated with the keys. * @hide */ -public class AppSearchBatchResult<KeyType, ValueType> implements Parcelable { +public final class AppSearchBatchResult<KeyType, ValueType> implements Parcelable { @NonNull private final Map<KeyType, ValueType> mSuccesses; @NonNull private final Map<KeyType, AppSearchResult<ValueType>> mFailures; - private AppSearchBatchResult( + AppSearchBatchResult( @NonNull Map<KeyType, ValueType> successes, @NonNull Map<KeyType, AppSearchResult<ValueType>> failures) { mSuccesses = successes; @@ -61,8 +63,8 @@ public class AppSearchBatchResult<KeyType, ValueType> implements Parcelable { } /** - * Returns a {@link Map} of all successful keys mapped to the successful {@link ValueType} - * values they produced. + * Returns a {@link Map} of all successful keys mapped to the successful + * {@link AppSearchResult}s they produced. * * <p>The values of the {@link Map} will not be {@code null}. */ @@ -82,6 +84,22 @@ public class AppSearchBatchResult<KeyType, ValueType> implements Parcelable { return mFailures; } + /** + * Asserts that this {@link AppSearchBatchResult} has no failures. + * @hide + */ + public void checkSuccess() { + if (!isSuccess()) { + throw new IllegalStateException("AppSearchBatchResult has failures: " + this); + } + } + + @Override + @NonNull + public String toString() { + return "{\n successes: " + mSuccesses + "\n failures: " + mFailures + "\n}"; + } + @Override public int describeContents() { return 0; @@ -112,16 +130,18 @@ public class AppSearchBatchResult<KeyType, ValueType> implements Parcelable { public static final class Builder<KeyType, ValueType> { private final Map<KeyType, ValueType> mSuccesses = new ArrayMap<>(); private final Map<KeyType, AppSearchResult<ValueType>> mFailures = new ArrayMap<>(); - - /** Creates a new {@link Builder} for this {@link AppSearchBatchResult}. */ - public Builder() {} + private boolean mBuilt = false; /** * Associates the {@code key} with the given successful return value. * * <p>Any previous mapping for a key, whether success or failure, is deleted. */ - public Builder setSuccess(@NonNull KeyType key, @Nullable ValueType result) { + @NonNull + public Builder<KeyType, ValueType> setSuccess( + @NonNull KeyType key, @Nullable ValueType result) { + Preconditions.checkState(!mBuilt, "Builder has already been used"); + Preconditions.checkNotNull(key); return setResult(key, AppSearchResult.newSuccessfulResult(result)); } @@ -130,10 +150,13 @@ public class AppSearchBatchResult<KeyType, ValueType> implements Parcelable { * * <p>Any previous mapping for a key, whether success or failure, is deleted. */ - public Builder setFailure( + @NonNull + public Builder<KeyType, ValueType> setFailure( @NonNull KeyType key, @AppSearchResult.ResultCode int resultCode, @Nullable String errorMessage) { + Preconditions.checkState(!mBuilt, "Builder has already been used"); + Preconditions.checkNotNull(key); return setResult(key, AppSearchResult.newFailedResult(resultCode, errorMessage)); } @@ -143,7 +166,11 @@ public class AppSearchBatchResult<KeyType, ValueType> implements Parcelable { * <p>Any previous mapping for a key, whether success or failure, is deleted. */ @NonNull - public Builder setResult(@NonNull KeyType key, @NonNull AppSearchResult<ValueType> result) { + public Builder<KeyType, ValueType> setResult( + @NonNull KeyType key, @NonNull AppSearchResult<ValueType> result) { + Preconditions.checkState(!mBuilt, "Builder has already been used"); + Preconditions.checkNotNull(key); + Preconditions.checkNotNull(result); if (result.isSuccess()) { mSuccesses.put(key, result.getResultValue()); mFailures.remove(key); @@ -157,6 +184,8 @@ public class AppSearchBatchResult<KeyType, ValueType> implements Parcelable { /** Builds an {@link AppSearchBatchResult} from the contents of this {@link Builder}. */ @NonNull public AppSearchBatchResult<KeyType, ValueType> build() { + Preconditions.checkState(!mBuilt, "Builder has already been used"); + mBuilt = true; return new AppSearchBatchResult<>(mSuccesses, mFailures); } } diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchDocument.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchDocument.java deleted file mode 100644 index 7d2b64e5d882..000000000000 --- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchDocument.java +++ /dev/null @@ -1,698 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.app.appsearch; - -import android.annotation.CurrentTimeMillisLong; -import android.annotation.DurationMillisLong; -import android.annotation.IntRange; -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.util.ArrayMap; -import android.util.Log; - -import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.util.ArrayUtils; -import com.android.internal.util.Preconditions; - -import com.google.android.icing.proto.DocumentProto; -import com.google.android.icing.proto.PropertyProto; -import com.google.protobuf.ByteString; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.Objects; - -/** - * Represents a document unit. - * - * <p>Documents are constructed via {@link AppSearchDocument.Builder}. - * @hide - */ -public class AppSearchDocument { - private static final String TAG = "AppSearchDocument"; - - /** The default empty namespace.*/ - // TODO(adorokhine): Allow namespace to be specified in the document. - public static final String DEFAULT_NAMESPACE = ""; - - /** - * The maximum number of elements in a repeatable field. Will reject the request if exceed - * this limit. - */ - private static final int MAX_REPEATED_PROPERTY_LENGTH = 100; - - /** - * The maximum {@link String#length} of a {@link String} field. Will reject the request if - * {@link String}s longer than this. - */ - private static final int MAX_STRING_LENGTH = 20_000; - - /** - * Contains {@link AppSearchDocument} basic information (uri, schemaType etc) and properties - * ordered by keys. - */ - @NonNull - private final DocumentProto mProto; - - /** Contains all properties in {@link #mProto} to support getting properties via keys. */ - @NonNull - private final Map<String, Object> mProperties; - - /** - * Creates a new {@link AppSearchDocument}. - * @param proto Contains {@link AppSearchDocument} basic information (uri, schemaType etc) and - * properties ordered by keys. - * @param propertiesMap Contains all properties in {@link #mProto} to support get properties - * via keys. - */ - private AppSearchDocument(@NonNull DocumentProto proto, - @NonNull Map<String, Object> propertiesMap) { - mProto = proto; - mProperties = propertiesMap; - } - - /** - * Creates a new {@link AppSearchDocument} from an existing instance. - * - * <p>This method should be only used by constructor of a subclass. - */ - protected AppSearchDocument(@NonNull AppSearchDocument document) { - this(document.mProto, document.mProperties); - } - - /** @hide */ - AppSearchDocument(@NonNull DocumentProto documentProto) { - this(documentProto, new ArrayMap<>()); - for (int i = 0; i < documentProto.getPropertiesCount(); i++) { - PropertyProto property = documentProto.getProperties(i); - String name = property.getName(); - if (property.getStringValuesCount() > 0) { - String[] values = new String[property.getStringValuesCount()]; - for (int j = 0; j < values.length; j++) { - values[j] = property.getStringValues(j); - } - mProperties.put(name, values); - } else if (property.getInt64ValuesCount() > 0) { - long[] values = new long[property.getInt64ValuesCount()]; - for (int j = 0; j < values.length; j++) { - values[j] = property.getInt64Values(j); - } - mProperties.put(property.getName(), values); - } else if (property.getDoubleValuesCount() > 0) { - double[] values = new double[property.getDoubleValuesCount()]; - for (int j = 0; j < values.length; j++) { - values[j] = property.getDoubleValues(j); - } - mProperties.put(property.getName(), values); - } else if (property.getBooleanValuesCount() > 0) { - boolean[] values = new boolean[property.getBooleanValuesCount()]; - for (int j = 0; j < values.length; j++) { - values[j] = property.getBooleanValues(j); - } - mProperties.put(property.getName(), values); - } else if (property.getBytesValuesCount() > 0) { - byte[][] values = new byte[property.getBytesValuesCount()][]; - for (int j = 0; j < values.length; j++) { - values[j] = property.getBytesValues(j).toByteArray(); - } - mProperties.put(name, values); - } else if (property.getDocumentValuesCount() > 0) { - AppSearchDocument[] values = - new AppSearchDocument[property.getDocumentValuesCount()]; - for (int j = 0; j < values.length; j++) { - values[j] = new AppSearchDocument(property.getDocumentValues(j)); - } - mProperties.put(name, values); - } else { - throw new IllegalStateException("Unknown type of value: " + name); - } - } - } - - /** - * Returns the {@link DocumentProto} of the {@link AppSearchDocument}. - * - * <p>The {@link DocumentProto} contains {@link AppSearchDocument}'s basic information and all - * properties ordered by keys. - * @hide - */ - @NonNull - @VisibleForTesting - public DocumentProto getProto() { - return mProto; - } - - /** Returns the URI of the {@link AppSearchDocument}. */ - @NonNull - public String getUri() { - return mProto.getUri(); - } - - /** Returns the schema type of the {@link AppSearchDocument}. */ - @NonNull - public String getSchemaType() { - return mProto.getSchema(); - } - - /** - * Returns the creation timestamp in milliseconds of the {@link AppSearchDocument}. Value will - * be in the {@link System#currentTimeMillis()} time base. - */ - @CurrentTimeMillisLong - public long getCreationTimestampMillis() { - return mProto.getCreationTimestampMs(); - } - - /** - * Returns the TTL (Time To Live) of the {@link AppSearchDocument}, in milliseconds. - * - * <p>The default value is 0, which means the document is permanent and won't be auto-deleted - * until the app is uninstalled. - */ - @DurationMillisLong - public long getTtlMillis() { - return mProto.getTtlMs(); - } - - /** - * Returns the score of the {@link AppSearchDocument}. - * - * <p>The score is a query-independent measure of the document's quality, relative to other - * {@link AppSearchDocument}s of the same type. - * - * <p>The default value is 0. - */ - public int getScore() { - return mProto.getScore(); - } - - /** - * Retrieve a {@link String} value by key. - * - * @param key The key to look for. - * @return The first {@link String} associated with the given key or {@code null} if there - * is no such key or the value is of a different type. - */ - @Nullable - public String getPropertyString(@NonNull String key) { - String[] propertyArray = getPropertyStringArray(key); - if (ArrayUtils.isEmpty(propertyArray)) { - return null; - } - warnIfSinglePropertyTooLong("String", key, propertyArray.length); - return propertyArray[0]; - } - - /** - * Retrieve a {@code long} value by key. - * - * @param key The key to look for. - * @return The first {@code long} associated with the given key or default value {@code 0} if - * there is no such key or the value is of a different type. - */ - public long getPropertyLong(@NonNull String key) { - long[] propertyArray = getPropertyLongArray(key); - if (ArrayUtils.isEmpty(propertyArray)) { - return 0; - } - warnIfSinglePropertyTooLong("Long", key, propertyArray.length); - return propertyArray[0]; - } - - /** - * Retrieve a {@code double} value by key. - * - * @param key The key to look for. - * @return The first {@code double} associated with the given key or default value {@code 0.0} - * if there is no such key or the value is of a different type. - */ - public double getPropertyDouble(@NonNull String key) { - double[] propertyArray = getPropertyDoubleArray(key); - // TODO(tytytyww): Add support double array to ArraysUtils.isEmpty(). - if (propertyArray == null || propertyArray.length == 0) { - return 0.0; - } - warnIfSinglePropertyTooLong("Double", key, propertyArray.length); - return propertyArray[0]; - } - - /** - * Retrieve a {@code boolean} value by key. - * - * @param key The key to look for. - * @return The first {@code boolean} associated with the given key or default value - * {@code false} if there is no such key or the value is of a different type. - */ - public boolean getPropertyBoolean(@NonNull String key) { - boolean[] propertyArray = getPropertyBooleanArray(key); - if (ArrayUtils.isEmpty(propertyArray)) { - return false; - } - warnIfSinglePropertyTooLong("Boolean", key, propertyArray.length); - return propertyArray[0]; - } - - /** - * Retrieve a {@code byte[]} value by key. - * - * @param key The key to look for. - * @return The first {@code byte[]} associated with the given key or {@code null} if there - * is no such key or the value is of a different type. - */ - @Nullable - public byte[] getPropertyBytes(@NonNull String key) { - byte[][] propertyArray = getPropertyBytesArray(key); - if (ArrayUtils.isEmpty(propertyArray)) { - return null; - } - warnIfSinglePropertyTooLong("ByteArray", key, propertyArray.length); - return propertyArray[0]; - } - - /** - * Retrieve a {@link AppSearchDocument} value by key. - * - * @param key The key to look for. - * @return The first {@link AppSearchDocument} associated with the given key or {@code null} if - * there is no such key or the value is of a different type. - */ - @Nullable - public AppSearchDocument getPropertyDocument(@NonNull String key) { - AppSearchDocument[] propertyArray = getPropertyDocumentArray(key); - if (ArrayUtils.isEmpty(propertyArray)) { - return null; - } - warnIfSinglePropertyTooLong("Document", key, propertyArray.length); - return propertyArray[0]; - } - - /** Prints a warning to logcat if the given propertyLength is greater than 1. */ - private static void warnIfSinglePropertyTooLong( - @NonNull String propertyType, @NonNull String key, int propertyLength) { - if (propertyLength > 1) { - Log.w(TAG, "The value for \"" + key + "\" contains " + propertyLength - + " elements. Only the first one will be returned from " - + "getProperty" + propertyType + "(). Try getProperty" + propertyType - + "Array()."); - } - } - - /** - * Retrieve a repeated {@link String} property by key. - * - * @param key The key to look for. - * @return The {@code String[]} associated with the given key, or {@code null} if no value - * is set or the value is of a different type. - */ - @Nullable - public String[] getPropertyStringArray(@NonNull String key) { - return getAndCastPropertyArray(key, String[].class); - } - - /** - * Retrieve a repeated {@code long} property by key. - * - * @param key The key to look for. - * @return The {@code long[]} associated with the given key, or {@code null} if no value is - * set or the value is of a different type. - */ - @Nullable - public long[] getPropertyLongArray(@NonNull String key) { - return getAndCastPropertyArray(key, long[].class); - } - - /** - * Retrieve a repeated {@code double} property by key. - * - * @param key The key to look for. - * @return The {@code double[]} associated with the given key, or {@code null} if no value - * is set or the value is of a different type. - */ - @Nullable - public double[] getPropertyDoubleArray(@NonNull String key) { - return getAndCastPropertyArray(key, double[].class); - } - - /** - * Retrieve a repeated {@code boolean} property by key. - * - * @param key The key to look for. - * @return The {@code boolean[]} associated with the given key, or {@code null} if no value - * is set or the value is of a different type. - */ - @Nullable - public boolean[] getPropertyBooleanArray(@NonNull String key) { - return getAndCastPropertyArray(key, boolean[].class); - } - - /** - * Retrieve a {@code byte[][]} property by key. - * - * @param key The key to look for. - * @return The {@code byte[][]} associated with the given key, or {@code null} if no value - * is set or the value is of a different type. - */ - @Nullable - public byte[][] getPropertyBytesArray(@NonNull String key) { - return getAndCastPropertyArray(key, byte[][].class); - } - - /** - * Retrieve a repeated {@link AppSearchDocument} property by key. - * - * @param key The key to look for. - * @return The {@link AppSearchDocument[]} associated with the given key, or {@code null} if no - * value is set or the value is of a different type. - */ - @Nullable - public AppSearchDocument[] getPropertyDocumentArray(@NonNull String key) { - return getAndCastPropertyArray(key, AppSearchDocument[].class); - } - - /** - * Gets a repeated property of the given key, and casts it to the given class type, which - * must be an array class type. - */ - @Nullable - private <T> T getAndCastPropertyArray(@NonNull String key, @NonNull Class<T> tClass) { - Object value = mProperties.get(key); - if (value == null) { - return null; - } - try { - return tClass.cast(value); - } catch (ClassCastException e) { - Log.w(TAG, "Error casting to requested type for key \"" + key + "\"", e); - return null; - } - } - - @Override - public boolean equals(@Nullable Object other) { - // Check only proto's equality is sufficient here since all properties in - // mProperties are ordered by keys and stored in proto. - if (this == other) { - return true; - } - if (!(other instanceof AppSearchDocument)) { - return false; - } - AppSearchDocument otherDocument = (AppSearchDocument) other; - return this.mProto.equals(otherDocument.mProto); - } - - @Override - public int hashCode() { - // Hash only proto is sufficient here since all properties in mProperties are ordered by - // keys and stored in proto. - return mProto.hashCode(); - } - - @Override - public String toString() { - return mProto.toString(); - } - - /** - * The builder class for {@link AppSearchDocument}. - * - * @param <BuilderType> Type of subclass who extend this. - */ - public static class Builder<BuilderType extends Builder> { - - private final Map<String, Object> mProperties = new ArrayMap<>(); - private final DocumentProto.Builder mProtoBuilder = DocumentProto.newBuilder(); - private final BuilderType mBuilderTypeInstance; - - /** - * Creates a new {@link AppSearchDocument.Builder}. - * - * <p>The URI is a unique string opaque to AppSearch. - * - * @param uri The uri of {@link AppSearchDocument}. - * @param schemaType The schema type of the {@link AppSearchDocument}. The passed-in - * {@code schemaType} must be defined using {@link AppSearchManager#setSchema} prior - * to inserting a document of this {@code schemaType} into the AppSearch index using - * {@link AppSearchManager#putDocuments(List)}. Otherwise, the document will be - * rejected by {@link AppSearchManager#putDocuments(List)}. - */ - public Builder(@NonNull String uri, @NonNull String schemaType) { - mBuilderTypeInstance = (BuilderType) this; - mProtoBuilder.setUri(uri).setSchema(schemaType).setNamespace(DEFAULT_NAMESPACE); - // Set current timestamp for creation timestamp by default. - setCreationTimestampMillis(System.currentTimeMillis()); - } - - /** - * Sets the score of the {@link AppSearchDocument}. - * - * <p>The score is a query-independent measure of the document's quality, relative to - * other {@link AppSearchDocument}s of the same type. - * - * @throws IllegalArgumentException If the provided value is negative. - */ - @NonNull - public BuilderType setScore(@IntRange(from = 0, to = Integer.MAX_VALUE) int score) { - if (score < 0) { - throw new IllegalArgumentException("Document score cannot be negative."); - } - mProtoBuilder.setScore(score); - return mBuilderTypeInstance; - } - - /** - * Set the creation timestamp in milliseconds of the {@link AppSearchDocument}. Should be - * set using a value obtained from the {@link System#currentTimeMillis()} time base. - */ - @NonNull - public BuilderType setCreationTimestampMillis( - @CurrentTimeMillisLong long creationTimestampMillis) { - mProtoBuilder.setCreationTimestampMs(creationTimestampMillis); - return mBuilderTypeInstance; - } - - /** - * Set the TTL (Time To Live) of the {@link AppSearchDocument}, in milliseconds. - * - * <p>After this many milliseconds since the {@link #setCreationTimestampMillis(long)} - * creation timestamp}, the document is deleted. - * - * @param ttlMillis A non-negative duration in milliseconds. - * @throws IllegalArgumentException If the provided value is negative. - */ - @NonNull - public BuilderType setTtlMillis(@DurationMillisLong long ttlMillis) { - Preconditions.checkArgumentNonNegative( - ttlMillis, "Document ttlMillis cannot be negative."); - mProtoBuilder.setTtlMs(ttlMillis); - return mBuilderTypeInstance; - } - - /** - * Sets one or multiple {@code String} values for a property, replacing its previous - * values. - * - * @param key The key associated with the {@code values}. - * @param values The {@code String} values of the property. - */ - @NonNull - public BuilderType setProperty(@NonNull String key, @NonNull String... values) { - putInPropertyMap(key, values); - return mBuilderTypeInstance; - } - - /** - * Sets one or multiple {@code boolean} values for a property, replacing its previous - * values. - * - * @param key The key associated with the {@code values}. - * @param values The {@code boolean} values of the property. - */ - @NonNull - public BuilderType setProperty(@NonNull String key, @NonNull boolean... values) { - putInPropertyMap(key, values); - return mBuilderTypeInstance; - } - - /** - * Sets one or multiple {@code long} values for a property, replacing its previous - * values. - * - * @param key The key associated with the {@code values}. - * @param values The {@code long} values of the property. - */ - @NonNull - public BuilderType setProperty(@NonNull String key, @NonNull long... values) { - putInPropertyMap(key, values); - return mBuilderTypeInstance; - } - - /** - * Sets one or multiple {@code double} values for a property, replacing its previous - * values. - * - * @param key The key associated with the {@code values}. - * @param values The {@code double} values of the property. - */ - @NonNull - public BuilderType setProperty(@NonNull String key, @NonNull double... values) { - putInPropertyMap(key, values); - return mBuilderTypeInstance; - } - - /** - * Sets one or multiple {@code byte[]} for a property, replacing its previous values. - * - * @param key The key associated with the {@code values}. - * @param values The {@code byte[]} of the property. - */ - @NonNull - public BuilderType setProperty(@NonNull String key, @NonNull byte[]... values) { - putInPropertyMap(key, values); - return mBuilderTypeInstance; - } - - /** - * Sets one or multiple {@link AppSearchDocument} values for a property, replacing its - * previous values. - * - * @param key The key associated with the {@code values}. - * @param values The {@link AppSearchDocument} values of the property. - */ - @NonNull - public BuilderType setProperty(@NonNull String key, @NonNull AppSearchDocument... values) { - putInPropertyMap(key, values); - return mBuilderTypeInstance; - } - - private void putInPropertyMap(@NonNull String key, @NonNull String[] values) - throws IllegalArgumentException { - Objects.requireNonNull(key); - Objects.requireNonNull(values); - validateRepeatedPropertyLength(key, values.length); - for (int i = 0; i < values.length; i++) { - if (values[i] == null) { - throw new IllegalArgumentException("The String at " + i + " is null."); - } else if (values[i].length() > MAX_STRING_LENGTH) { - throw new IllegalArgumentException("The String at " + i + " length is: " - + values[i].length() + ", which exceeds length limit: " - + MAX_STRING_LENGTH + "."); - } - } - mProperties.put(key, values); - } - - private void putInPropertyMap(@NonNull String key, @NonNull boolean[] values) { - Objects.requireNonNull(key); - Objects.requireNonNull(values); - validateRepeatedPropertyLength(key, values.length); - mProperties.put(key, values); - } - - private void putInPropertyMap(@NonNull String key, @NonNull double[] values) { - Objects.requireNonNull(key); - Objects.requireNonNull(values); - validateRepeatedPropertyLength(key, values.length); - mProperties.put(key, values); - } - - private void putInPropertyMap(@NonNull String key, @NonNull long[] values) { - Objects.requireNonNull(key); - Objects.requireNonNull(values); - validateRepeatedPropertyLength(key, values.length); - mProperties.put(key, values); - } - - private void putInPropertyMap(@NonNull String key, @NonNull byte[][] values) { - Objects.requireNonNull(key); - Objects.requireNonNull(values); - validateRepeatedPropertyLength(key, values.length); - mProperties.put(key, values); - } - - private void putInPropertyMap(@NonNull String key, @NonNull AppSearchDocument[] values) { - Objects.requireNonNull(key); - Objects.requireNonNull(values); - for (int i = 0; i < values.length; i++) { - if (values[i] == null) { - throw new IllegalArgumentException("The document at " + i + " is null."); - } - } - validateRepeatedPropertyLength(key, values.length); - mProperties.put(key, values); - } - - private static void validateRepeatedPropertyLength(@NonNull String key, int length) { - if (length == 0) { - throw new IllegalArgumentException("The input array is empty."); - } else if (length > MAX_REPEATED_PROPERTY_LENGTH) { - throw new IllegalArgumentException( - "Repeated property \"" + key + "\" has length " + length - + ", which exceeds the limit of " - + MAX_REPEATED_PROPERTY_LENGTH); - } - } - - /** Builds the {@link AppSearchDocument} object. */ - @NonNull - public AppSearchDocument build() { - // Build proto by sorting the keys in mProperties to exclude the influence of - // order. Therefore documents will generate same proto as long as the contents are - // same. Note that the order of repeated fields is still preserved. - ArrayList<String> keys = new ArrayList<>(mProperties.keySet()); - Collections.sort(keys); - for (int i = 0; i < keys.size(); i++) { - String name = keys.get(i); - Object values = mProperties.get(name); - PropertyProto.Builder propertyProto = PropertyProto.newBuilder().setName(name); - if (values instanceof boolean[]) { - for (boolean value : (boolean[]) values) { - propertyProto.addBooleanValues(value); - } - } else if (values instanceof long[]) { - for (long value : (long[]) values) { - propertyProto.addInt64Values(value); - } - } else if (values instanceof double[]) { - for (double value : (double[]) values) { - propertyProto.addDoubleValues(value); - } - } else if (values instanceof String[]) { - for (String value : (String[]) values) { - propertyProto.addStringValues(value); - } - } else if (values instanceof AppSearchDocument[]) { - for (AppSearchDocument value : (AppSearchDocument[]) values) { - propertyProto.addDocumentValues(value.getProto()); - } - } else if (values instanceof byte[][]) { - for (byte[] value : (byte[][]) values) { - propertyProto.addBytesValues(ByteString.copyFrom(value)); - } - } else { - throw new IllegalStateException( - "Property \"" + name + "\" has unsupported value type \"" - + values.getClass().getSimpleName() + "\""); - } - mProtoBuilder.addProperties(propertyProto); - } - return new AppSearchDocument(mProtoBuilder.build(), mProperties); - } - } -} diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchEmail.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchEmail.java index b13dd9f48c8d..5f2fabe52929 100644 --- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchEmail.java +++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchEmail.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 The Android Open Source Project + * Copyright 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,20 +16,26 @@ package android.app.appsearch; + import android.annotation.NonNull; import android.annotation.Nullable; + import android.app.appsearch.AppSearchSchema.PropertyConfig; /** - * Encapsulates a {@link AppSearchDocument} that represent an email. + * Encapsulates a {@link GenericDocument} that represent an email. * - * <p>This class is a higher level implement of {@link AppSearchDocument}. + * <p>This class is a higher level implement of {@link GenericDocument}. * * <p>This class will eventually migrate to Jetpack, where it will become public API. * * @hide */ -public class AppSearchEmail extends AppSearchDocument { + +public class AppSearchEmail extends GenericDocument { + /** The name of the schema type for {@link AppSearchEmail} documents.*/ + public static final String SCHEMA_TYPE = "builtin:Email"; + private static final String KEY_FROM = "from"; private static final String KEY_TO = "to"; private static final String KEY_CC = "cc"; @@ -37,46 +43,43 @@ public class AppSearchEmail extends AppSearchDocument { private static final String KEY_SUBJECT = "subject"; private static final String KEY_BODY = "body"; - /** The name of the schema type for {@link AppSearchEmail} documents.*/ - public static final String SCHEMA_TYPE = "builtin:Email"; - public static final AppSearchSchema SCHEMA = new AppSearchSchema.Builder(SCHEMA_TYPE) - .addProperty(new AppSearchSchema.PropertyConfig.Builder(KEY_FROM) + .addProperty(new PropertyConfig.Builder(KEY_FROM) .setDataType(PropertyConfig.DATA_TYPE_STRING) .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL) .setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN) .setIndexingType(PropertyConfig.INDEXING_TYPE_PREFIXES) .build() - ).addProperty(new AppSearchSchema.PropertyConfig.Builder(KEY_TO) + ).addProperty(new PropertyConfig.Builder(KEY_TO) .setDataType(PropertyConfig.DATA_TYPE_STRING) .setCardinality(PropertyConfig.CARDINALITY_REPEATED) .setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN) .setIndexingType(PropertyConfig.INDEXING_TYPE_PREFIXES) .build() - ).addProperty(new AppSearchSchema.PropertyConfig.Builder(KEY_CC) + ).addProperty(new PropertyConfig.Builder(KEY_CC) .setDataType(PropertyConfig.DATA_TYPE_STRING) .setCardinality(PropertyConfig.CARDINALITY_REPEATED) .setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN) .setIndexingType(PropertyConfig.INDEXING_TYPE_PREFIXES) .build() - ).addProperty(new AppSearchSchema.PropertyConfig.Builder(KEY_BCC) + ).addProperty(new PropertyConfig.Builder(KEY_BCC) .setDataType(PropertyConfig.DATA_TYPE_STRING) .setCardinality(PropertyConfig.CARDINALITY_REPEATED) .setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN) .setIndexingType(PropertyConfig.INDEXING_TYPE_PREFIXES) .build() - ).addProperty(new AppSearchSchema.PropertyConfig.Builder(KEY_SUBJECT) + ).addProperty(new PropertyConfig.Builder(KEY_SUBJECT) .setDataType(PropertyConfig.DATA_TYPE_STRING) .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL) .setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN) .setIndexingType(PropertyConfig.INDEXING_TYPE_PREFIXES) .build() - ).addProperty(new AppSearchSchema.PropertyConfig.Builder(KEY_BODY) + ).addProperty(new PropertyConfig.Builder(KEY_BODY) .setDataType(PropertyConfig.DATA_TYPE_STRING) .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL) .setTokenizerType(PropertyConfig.TOKENIZER_TYPE_PLAIN) @@ -87,12 +90,11 @@ public class AppSearchEmail extends AppSearchDocument { /** * Creates a new {@link AppSearchEmail} from the contents of an existing - * {@link AppSearchDocument}. + * {@link GenericDocument}. * - * @param document The {@link AppSearchDocument} containing the email content. - * @hide + * @param document The {@link GenericDocument} containing the email content. */ - public AppSearchEmail(@NonNull AppSearchDocument document) { + public AppSearchEmail(@NonNull GenericDocument document) { super(document); } @@ -101,7 +103,6 @@ public class AppSearchEmail extends AppSearchDocument { * * @return Returns the subject of {@link AppSearchEmail} or {@code null} if it's not been set * yet. - * @hide */ @Nullable public String getFrom() { @@ -113,7 +114,6 @@ public class AppSearchEmail extends AppSearchDocument { * * @return Returns the destination addresses of {@link AppSearchEmail} or {@code null} if it's * not been set yet. - * @hide */ @Nullable public String[] getTo() { @@ -125,7 +125,6 @@ public class AppSearchEmail extends AppSearchDocument { * * @return Returns the CC list of {@link AppSearchEmail} or {@code null} if it's not been set * yet. - * @hide */ @Nullable public String[] getCc() { @@ -137,7 +136,6 @@ public class AppSearchEmail extends AppSearchDocument { * * @return Returns the BCC list of {@link AppSearchEmail} or {@code null} if it's not been set * yet. - * @hide */ @Nullable public String[] getBcc() { @@ -149,7 +147,6 @@ public class AppSearchEmail extends AppSearchDocument { * * @return Returns the value subject of {@link AppSearchEmail} or {@code null} if it's not been * set yet. - * @hide */ @Nullable public String getSubject() { @@ -160,7 +157,6 @@ public class AppSearchEmail extends AppSearchDocument { * Get the body of {@link AppSearchEmail}. * * @return Returns the body of {@link AppSearchEmail} or {@code null} if it's not been set yet. - * @hide */ @Nullable public String getBody() { @@ -169,14 +165,12 @@ public class AppSearchEmail extends AppSearchDocument { /** * The builder class for {@link AppSearchEmail}. - * @hide */ - public static class Builder extends AppSearchDocument.Builder<AppSearchEmail.Builder> { + public static class Builder extends GenericDocument.Builder<AppSearchEmail.Builder> { /** * Create a new {@link AppSearchEmail.Builder} * @param uri The Uri of the Email. - * @hide */ public Builder(@NonNull String uri) { super(uri, SCHEMA_TYPE); @@ -184,7 +178,6 @@ public class AppSearchEmail extends AppSearchDocument { /** * Set the from address of {@link AppSearchEmail} - * @hide */ @NonNull public AppSearchEmail.Builder setFrom(@NonNull String from) { @@ -194,7 +187,6 @@ public class AppSearchEmail extends AppSearchDocument { /** * Set the destination address of {@link AppSearchEmail} - * @hide */ @NonNull public AppSearchEmail.Builder setTo(@NonNull String... to) { @@ -204,7 +196,6 @@ public class AppSearchEmail extends AppSearchDocument { /** * Set the CC list of {@link AppSearchEmail} - * @hide */ @NonNull public AppSearchEmail.Builder setCc(@NonNull String... cc) { @@ -214,7 +205,6 @@ public class AppSearchEmail extends AppSearchDocument { /** * Set the BCC list of {@link AppSearchEmail} - * @hide */ @NonNull public AppSearchEmail.Builder setBcc(@NonNull String... bcc) { @@ -224,7 +214,6 @@ public class AppSearchEmail extends AppSearchDocument { /** * Set the subject of {@link AppSearchEmail} - * @hide */ @NonNull public AppSearchEmail.Builder setSubject(@NonNull String subject) { @@ -234,7 +223,6 @@ public class AppSearchEmail extends AppSearchDocument { /** * Set the body of {@link AppSearchEmail} - * @hide */ @NonNull public AppSearchEmail.Builder setBody(@NonNull String body) { @@ -242,11 +230,7 @@ public class AppSearchEmail extends AppSearchDocument { return this; } - /** - * Builds the {@link AppSearchEmail} object. - * - * @hide - */ + /** Builds the {@link AppSearchEmail} object. */ @NonNull @Override public AppSearchEmail build() { diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java index ad51a5c4158e..18bc59b91aa0 100644 --- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java +++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java @@ -22,14 +22,9 @@ import android.os.Bundle; import android.os.RemoteException; import com.android.internal.infra.AndroidFuture; - -import com.google.android.icing.proto.DocumentProto; -import com.google.android.icing.proto.SearchResultProto; -import com.google.android.icing.proto.StatusProto; -import com.google.protobuf.InvalidProtocolBufferException; +import com.android.internal.util.Preconditions; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.concurrent.ExecutionException; @@ -45,6 +40,7 @@ import java.util.concurrent.ExecutionException; // TODO(b/148046169): This class header needs a detailed example/tutorial. @SystemService(Context.APP_SEARCH_SERVICE) public class AppSearchManager { + private static final String DEFAULT_DATABASE = ""; private final IAppSearchManager mService; /** @hide */ @@ -78,8 +74,8 @@ public class AppSearchManager { * <li>Removal of an existing type * <li>Removal of a property from a type * <li>Changing the data type ({@code boolean}, {@code long}, etc.) of an existing property - * <li>For properties of {@code AppSearchDocument} type, changing the schema type of - * {@code AppSearchDocument}s of that property + * <li>For properties of {@code GenericDocument} type, changing the schema type of + * {@code GenericDocument}s of that property * <li>Changing the cardinality of a data type to be more restrictive (e.g. changing an * {@link android.app.appsearch.AppSearchSchema.PropertyConfig#CARDINALITY_OPTIONAL * OPTIONAL} property into a @@ -101,46 +97,23 @@ public class AppSearchManager { * <p>It is a no-op to set the same schema as has been previously set; this is handled * efficiently. * - * @param schemas The schema configs for the types used by the calling app. - * @return the result of performing this operation. - * - * @hide - */ - @NonNull - public AppSearchResult<Void> setSchema(@NonNull AppSearchSchema... schemas) { - return setSchema(Arrays.asList(schemas), /*forceOverride=*/false); - } - - /** - * Sets the schema being used by documents provided to the {@link #putDocuments} method. - * - * <p>This method is similar to {@link #setSchema(AppSearchSchema...)}, except for the - * {@code forceOverride} parameter. If a backwards-incompatible schema is specified but the - * {@code forceOverride} parameter is set to {@code true}, instead of returning an - * {@link AppSearchResult} with the {@link AppSearchResult#RESULT_INVALID_SCHEMA} code, all - * documents which are not compatible with the new schema will be deleted and the incompatible - * schema will be applied. - * - * @param schemas The schema configs for the types used by the calling app. - * @param forceOverride Whether to force the new schema to be applied even if there are - * incompatible changes versus the previously set schema. Documents which are incompatible - * with the new schema will be deleted. + * @param request The schema update request. * @return the result of performing this operation. * * @hide */ @NonNull - public AppSearchResult<Void> setSchema( - @NonNull List<AppSearchSchema> schemas, boolean forceOverride) { + public AppSearchResult<Void> setSchema(@NonNull SetSchemaRequest request) { + Preconditions.checkNotNull(request); // TODO: This should use com.android.internal.infra.RemoteStream or another mechanism to // avoid binder limits. - List<Bundle> schemaBundles = new ArrayList<>(schemas.size()); - for (AppSearchSchema schema : schemas) { + List<Bundle> schemaBundles = new ArrayList<>(request.getSchemas().size()); + for (AppSearchSchema schema : request.getSchemas()) { schemaBundles.add(schema.getBundle()); } AndroidFuture<AppSearchResult> future = new AndroidFuture<>(); try { - mService.setSchema(schemaBundles, forceOverride, future); + mService.setSchema(DEFAULT_DATABASE, schemaBundles, request.isForceOverride(), future); } catch (RemoteException e) { future.completeExceptionally(e); } @@ -148,31 +121,32 @@ public class AppSearchManager { } /** - * Index {@link AppSearchDocument}s into AppSearch. + * Index {@link GenericDocument}s into AppSearch. * * <p>You should not call this method directly; instead, use the * {@code AppSearch#putDocuments()} API provided by JetPack. * - * <p>Each {@link AppSearchDocument}'s {@code schemaType} field must be set to the name of a + * <p>Each {@link GenericDocument}'s {@code schemaType} field must be set to the name of a * schema type previously registered via the {@link #setSchema} method. * - * @param documents {@link AppSearchDocument}s that need to be indexed. - * @return An {@link AppSearchBatchResult} mapping the document URIs to {@link Void} if they - * were successfully indexed, or a {@link Throwable} describing the failure if they could - * not be indexed. + * @param request {@link PutDocumentsRequest} containing documents to be indexed + * @return The pending result of performing this operation. The keys of the returned + * {@link AppSearchBatchResult} are the URIs of the input documents. The values are + * {@code null} if they were successfully indexed, or a failed {@link AppSearchResult} + * otherwise. * @hide */ - public AppSearchBatchResult<String, Void> putDocuments( - @NonNull List<AppSearchDocument> documents) { + public AppSearchBatchResult<String, Void> putDocuments(@NonNull PutDocumentsRequest request) { // TODO(b/146386470): Transmit these documents as a RemoteStream instead of sending them in // one big list. - List<byte[]> documentsBytes = new ArrayList<>(documents.size()); - for (AppSearchDocument document : documents) { - documentsBytes.add(document.getProto().toByteArray()); + List<GenericDocument> documents = request.getDocuments(); + List<Bundle> documentBundles = new ArrayList<>(documents.size()); + for (int i = 0; i < documents.size(); i++) { + documentBundles.add(documents.get(i).getBundle()); } AndroidFuture<AppSearchBatchResult> future = new AndroidFuture<>(); try { - mService.putDocuments(documentsBytes, future); + mService.putDocuments(DEFAULT_DATABASE, documentBundles, future); } catch (RemoteException e) { future.completeExceptionally(e); } @@ -180,65 +154,59 @@ public class AppSearchManager { } /** - * Retrieves {@link AppSearchDocument}s by URI. + * Retrieves {@link GenericDocument}s by URI. * * <p>You should not call this method directly; instead, use the * {@code AppSearch#getDocuments()} API provided by JetPack. * - * @param uris URIs of the documents to look up. - * @return An {@link AppSearchBatchResult} mapping the document URIs to - * {@link AppSearchDocument} values if they were successfully retrieved, a {@code null} - * failure if they were not found, or a {@link Throwable} failure describing the problem if - * an error occurred. + * @param request {@link GetByUriRequest} containing URIs to be retrieved. + * @return The pending result of performing this operation. The keys of the returned + * {@link AppSearchBatchResult} are the input URIs. The values are the returned + * {@link GenericDocument}s on success, or a failed {@link AppSearchResult} otherwise. + * URIs that are not found will return a failed {@link AppSearchResult} with a result code + * of {@link AppSearchResult#RESULT_NOT_FOUND}. */ - public AppSearchBatchResult<String, AppSearchDocument> getDocuments( - @NonNull List<String> uris) { + public AppSearchBatchResult<String, GenericDocument> getByUri( + @NonNull GetByUriRequest request) { // TODO(b/146386470): Transmit the result documents as a RemoteStream instead of sending // them in one big list. + List<String> uris = new ArrayList<>(request.getUris()); AndroidFuture<AppSearchBatchResult> future = new AndroidFuture<>(); try { - mService.getDocuments(uris, future); + mService.getDocuments(DEFAULT_DATABASE, request.getNamespace(), uris, future); } catch (RemoteException e) { future.completeExceptionally(e); } - // Deserialize the protos into Document objects - AppSearchBatchResult<String, byte[]> protoResults = getFutureOrThrow(future); - AppSearchBatchResult.Builder<String, AppSearchDocument> documentResultBuilder = + // Translate from document bundles to GenericDocument instances + AppSearchBatchResult<String, Bundle> bundleResult = getFutureOrThrow(future); + AppSearchBatchResult.Builder<String, GenericDocument> documentResultBuilder = new AppSearchBatchResult.Builder<>(); // Translate successful results - for (Map.Entry<String, byte[]> protoResult : protoResults.getSuccesses().entrySet()) { - DocumentProto documentProto; + for (Map.Entry<String, Bundle> bundleEntry : bundleResult.getSuccesses().entrySet()) { + GenericDocument document; try { - documentProto = DocumentProto.parseFrom(protoResult.getValue()); - } catch (InvalidProtocolBufferException e) { - documentResultBuilder.setFailure( - protoResult.getKey(), AppSearchResult.RESULT_IO_ERROR, e.getMessage()); - continue; - } - AppSearchDocument document; - try { - document = new AppSearchDocument(documentProto); + document = new GenericDocument(bundleEntry.getValue()); } catch (Throwable t) { // These documents went through validation, so how could this fail? We must have // done something wrong. documentResultBuilder.setFailure( - protoResult.getKey(), + bundleEntry.getKey(), AppSearchResult.RESULT_INTERNAL_ERROR, t.getMessage()); continue; } - documentResultBuilder.setSuccess(protoResult.getKey(), document); + documentResultBuilder.setSuccess(bundleEntry.getKey(), document); } // Translate failed results - for (Map.Entry<String, AppSearchResult<byte[]>> protoResult : - protoResults.getFailures().entrySet()) { + for (Map.Entry<String, AppSearchResult<Bundle>> bundleEntry : + bundleResult.getFailures().entrySet()) { documentResultBuilder.setFailure( - protoResult.getKey(), - protoResult.getValue().getResultCode(), - protoResult.getValue().getErrorMessage()); + bundleEntry.getKey(), + bundleEntry.getValue().getResultCode(), + bundleEntry.getValue().getErrorMessage()); } return documentResultBuilder.build(); @@ -287,56 +255,46 @@ public class AppSearchManager { * @hide */ @NonNull - public AppSearchResult<SearchResults> query( + public AppSearchResult<List<SearchResult>> query( @NonNull String queryExpression, @NonNull SearchSpec searchSpec) { // TODO(b/146386470): Transmit the result documents as a RemoteStream instead of sending // them in one big list. - AndroidFuture<AppSearchResult> searchResultFuture = new AndroidFuture<>(); + AndroidFuture<AppSearchResult> searchResultsFuture = new AndroidFuture<>(); try { - mService.query(queryExpression, searchSpec.getBundle(), searchResultFuture); + mService.query(DEFAULT_DATABASE, queryExpression, + searchSpec.getBundle(), searchResultsFuture); } catch (RemoteException e) { - searchResultFuture.completeExceptionally(e); + searchResultsFuture.completeExceptionally(e); } - // Deserialize the protos into Document objects - AppSearchResult<byte[]> searchResultBytes = getFutureOrThrow(searchResultFuture); - if (!searchResultBytes.isSuccess()) { + // Translate the list of Bundle into a list of SearchResult + AppSearchResult<SearchResults> searchResultsResult = getFutureOrThrow(searchResultsFuture); + if (!searchResultsResult.isSuccess()) { return AppSearchResult.newFailedResult( - searchResultBytes.getResultCode(), searchResultBytes.getErrorMessage()); + searchResultsResult.getResultCode(), searchResultsResult.getErrorMessage()); } - SearchResultProto searchResultProto; - try { - searchResultProto = SearchResultProto.parseFrom(searchResultBytes.getResultValue()); - } catch (InvalidProtocolBufferException e) { - return AppSearchResult.newFailedResult( - AppSearchResult.RESULT_INTERNAL_ERROR, e.getMessage()); - } - if (searchResultProto.getStatus().getCode() != StatusProto.Code.OK) { - // This should never happen; AppSearchManagerService should catch failed searchResults - // entries and transmit them as a failed AppSearchResult. - return AppSearchResult.newFailedResult( - AppSearchResult.RESULT_INTERNAL_ERROR, - searchResultProto.getStatus().getMessage()); - } - - return AppSearchResult.newSuccessfulResult(new SearchResults(searchResultProto)); + SearchResults searchResults = searchResultsResult.getResultValue(); + return AppSearchResult.newSuccessfulResult(searchResults.mResults); } /** - * Deletes {@link AppSearchDocument}s by URI. + * Deletes {@link GenericDocument}s by URI. * * <p>You should not call this method directly; instead, use the {@code AppSearch#delete()} API * provided by JetPack. * - * @param uris URIs of the documents to delete - * @return An {@link AppSearchBatchResult} mapping each URI to a {@code null} success if - * deletion was successful, to a {@code null} failure if the document did not exist, or to a - * {@code throwable} failure if deletion failed for another reason. + * @param request Request containing URIs to be removed. + * @return The pending result of performing this operation. The keys of the returned + * {@link AppSearchBatchResult} are the input URIs. The values are {@code null} on success, + * or a failed {@link AppSearchResult} otherwise. URIs that are not found will return a + * failed {@link AppSearchResult} with a result code of + * {@link AppSearchResult#RESULT_NOT_FOUND}. */ - public AppSearchBatchResult<String, Void> delete(@NonNull List<String> uris) { + public AppSearchBatchResult<String, Void> removeByUri(@NonNull RemoveByUriRequest request) { + List<String> uris = new ArrayList<>(request.getUris()); AndroidFuture<AppSearchBatchResult> future = new AndroidFuture<>(); try { - mService.delete(uris, future); + mService.delete(DEFAULT_DATABASE, request.getNamespace(), uris, future); } catch (RemoteException e) { future.completeExceptionally(e); } @@ -344,7 +302,7 @@ public class AppSearchManager { } /** - * Deletes {@link android.app.appsearch.AppSearch.Document}s by schema type. + * Deletes {@link android.app.appsearch.GenericDocument}s by schema type. * * <p>You should not call this method directly; instead, use the * {@code AppSearch#deleteByType()} API provided by JetPack. @@ -357,7 +315,7 @@ public class AppSearchManager { public AppSearchBatchResult<String, Void> deleteByTypes(@NonNull List<String> schemaTypes) { AndroidFuture<AppSearchBatchResult> future = new AndroidFuture<>(); try { - mService.deleteByTypes(schemaTypes, future); + mService.deleteByTypes(DEFAULT_DATABASE, schemaTypes, future); } catch (RemoteException e) { future.completeExceptionally(e); } @@ -368,7 +326,7 @@ public class AppSearchManager { public AppSearchResult<Void> deleteAll() { AndroidFuture<AppSearchResult> future = new AndroidFuture<>(); try { - mService.deleteAll(future); + mService.deleteAll(DEFAULT_DATABASE, future); } catch (RemoteException e) { future.completeExceptionally(e); } diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchResult.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchResult.java index 7f3834852eda..979eab90a980 100644 --- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchResult.java +++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchResult.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 The Android Open Source Project + * Copyright 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -32,9 +32,12 @@ import java.util.Objects; * @param <ValueType> The type of result object for successful calls. * @hide */ -public class AppSearchResult<ValueType> implements Parcelable { - /** Result codes from {@link AppSearchManager} methods. */ - @IntDef(prefix = {"RESULT_"}, value = { +public final class AppSearchResult<ValueType> implements Parcelable { + /** + * Result codes from {@link AppSearchManager} methods. + * @hide + */ + @IntDef(value = { RESULT_OK, RESULT_UNKNOWN_ERROR, RESULT_INTERNAL_ERROR, @@ -120,15 +123,18 @@ public class AppSearchResult<ValueType> implements Parcelable { } /** - * Returns the returned value associated with this result. + * Returns the result value associated with this result, if it was successful. * - * <p>If {@link #isSuccess} is {@code false}, the result value is always {@code null}. The value - * may be {@code null} even if {@link #isSuccess} is {@code true}. See the documentation of the - * particular {@link AppSearchManager} call producing this {@link AppSearchResult} for what is - * returned by {@link #getResultValue}. + * <p>See the documentation of the particular {@link AppSearchManager} call producing this + * {@link AppSearchResult} for what is placed in the result value by that call. + * + * @throws IllegalStateException if this {@link AppSearchResult} is not successful. */ @Nullable public ValueType getResultValue() { + if (!isSuccess()) { + throw new IllegalStateException("AppSearchResult is a failure: " + this); + } return mResultValue; } @@ -146,14 +152,14 @@ public class AppSearchResult<ValueType> implements Parcelable { } @Override - public boolean equals(Object other) { + public boolean equals(@Nullable Object other) { if (this == other) { return true; } if (!(other instanceof AppSearchResult)) { return false; } - AppSearchResult<?> otherResult = (AppSearchResult) other; + AppSearchResult<?> otherResult = (AppSearchResult<?>) other; return mResultCode == otherResult.mResultCode && Objects.equals(mResultValue, otherResult.mResultValue) && Objects.equals(mErrorMessage, otherResult.mErrorMessage); @@ -168,9 +174,9 @@ public class AppSearchResult<ValueType> implements Parcelable { @NonNull public String toString() { if (isSuccess()) { - return "AppSearchResult [SUCCESS]: " + mResultValue; + return "[SUCCESS]: " + mResultValue; } - return "AppSearchResult [FAILURE(" + mResultCode + ")]: " + mErrorMessage; + return "[FAILURE(" + mResultCode + ")]: " + mErrorMessage; } @Override diff --git a/apex/appsearch/framework/java/android/app/appsearch/GenericDocument.java b/apex/appsearch/framework/java/android/app/appsearch/GenericDocument.java new file mode 100644 index 000000000000..9fe2c67d00f2 --- /dev/null +++ b/apex/appsearch/framework/java/android/app/appsearch/GenericDocument.java @@ -0,0 +1,923 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app.appsearch; + +import android.annotation.SuppressLint; +import android.os.Bundle; +import android.util.Log; + +import android.annotation.IntRange; +import android.annotation.NonNull; +import android.annotation.Nullable; + +import android.app.appsearch.exceptions.AppSearchException; +import com.android.internal.util.Preconditions; + +import java.lang.reflect.Array; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Set; + +/** + * Represents a document unit. + * + * <p>Documents are constructed via {@link GenericDocument.Builder}. + * @hide + */ +public class GenericDocument { + private static final String TAG = "GenericDocument"; + + /** The default empty namespace.*/ + public static final String DEFAULT_NAMESPACE = ""; + + /** + * The maximum number of elements in a repeatable field. Will reject the request if exceed + * this limit. + */ + private static final int MAX_REPEATED_PROPERTY_LENGTH = 100; + + /** + * The maximum {@link String#length} of a {@link String} field. Will reject the request if + * {@link String}s longer than this. + */ + private static final int MAX_STRING_LENGTH = 20_000; + + /** The maximum number of indexed properties a document can have. */ + private static final int MAX_INDEXED_PROPERTIES = 16; + + /** The default score of document. */ + private static final int DEFAULT_SCORE = 0; + + /** The default time-to-live in millisecond of a document, which is infinity. */ + private static final long DEFAULT_TTL_MILLIS = 0L; + + /** @hide */ + + public static final String PROPERTIES_FIELD = "properties"; + + /** @hide */ + + public static final String BYTE_ARRAY_FIELD = "byteArray"; + + static final String SCHEMA_TYPE_FIELD = "schemaType"; + static final String URI_FIELD = "uri"; + static final String SCORE_FIELD = "score"; + static final String TTL_MILLIS_FIELD = "ttlMillis"; + static final String CREATION_TIMESTAMP_MILLIS_FIELD = "creationTimestampMillis"; + static final String NAMESPACE_FIELD = "namespace"; + + /** + * The maximum number of indexed properties a document can have. + * + * <p>Indexed properties are properties where the + * {@link android.app.appsearch.annotation.AppSearchDocument.Property#indexingType} constant is + * anything other than {@link + * android.app.appsearch.AppSearchSchema.PropertyConfig.IndexingType#INDEXING_TYPE_NONE}. + */ + public static int getMaxIndexedProperties() { + return MAX_INDEXED_PROPERTIES; + } + + /** Contains {@link GenericDocument} basic information (uri, schemaType etc).*/ + @NonNull + final Bundle mBundle; + + /** Contains all properties in {@link GenericDocument} to support getting properties via keys.*/ + @NonNull + private final Bundle mProperties; + + @NonNull + private final String mUri; + @NonNull + private final String mSchemaType; + private final long mCreationTimestampMillis; + @Nullable + private Integer mHashCode; + + /** + * Rebuilds a {@link GenericDocument} by the a bundle. + * @param bundle Contains {@link GenericDocument} basic information (uri, schemaType etc) and + * a properties bundle contains all properties in {@link GenericDocument} to + * support getting properties via keys. + * @hide + */ + + public GenericDocument(@NonNull Bundle bundle) { + Preconditions.checkNotNull(bundle); + mBundle = bundle; + mProperties = Preconditions.checkNotNull(bundle.getParcelable(PROPERTIES_FIELD)); + mUri = Preconditions.checkNotNull(mBundle.getString(URI_FIELD)); + mSchemaType = Preconditions.checkNotNull(mBundle.getString(SCHEMA_TYPE_FIELD)); + mCreationTimestampMillis = mBundle.getLong(CREATION_TIMESTAMP_MILLIS_FIELD, + System.currentTimeMillis()); + } + + /** + * Creates a new {@link GenericDocument} from an existing instance. + * + * <p>This method should be only used by constructor of a subclass. + */ + protected GenericDocument(@NonNull GenericDocument document) { + this(document.mBundle); + } + + /** + * Returns the {@link Bundle} populated by this builder. + * @hide + */ + + @NonNull + public Bundle getBundle() { + return mBundle; + } + + /** Returns the URI of the {@link GenericDocument}. */ + @NonNull + public String getUri() { + return mUri; + } + + /** Returns the namespace of the {@link GenericDocument}. */ + @NonNull + public String getNamespace() { + return mBundle.getString(NAMESPACE_FIELD, DEFAULT_NAMESPACE); + } + + /** Returns the schema type of the {@link GenericDocument}. */ + @NonNull + public String getSchemaType() { + return mSchemaType; + } + + /** Returns the creation timestamp of the {@link GenericDocument}, in milliseconds. */ + public long getCreationTimestampMillis() { + return mCreationTimestampMillis; + } + + /** + * Returns the TTL (Time To Live) of the {@link GenericDocument}, in milliseconds. + * + * <p>The default value is 0, which means the document is permanent and won't be auto-deleted + * until the app is uninstalled. + */ + public long getTtlMillis() { + return mBundle.getLong(TTL_MILLIS_FIELD, DEFAULT_TTL_MILLIS); + } + + /** + * Returns the score of the {@link GenericDocument}. + * + * <p>The score is a query-independent measure of the document's quality, relative to other + * {@link GenericDocument}s of the same type. + * + * <p>The default value is 0. + */ + public int getScore() { + return mBundle.getInt(SCORE_FIELD, DEFAULT_SCORE); + } + + /** + * Retrieves a {@link String} value by key. + * + * @param key The key to look for. + * @return The first {@link String} associated with the given key or {@code null} if there + * is no such key or the value is of a different type. + */ + @Nullable + public String getPropertyString(@NonNull String key) { + Preconditions.checkNotNull(key); + String[] propertyArray = getPropertyStringArray(key); + if (propertyArray == null || propertyArray.length == 0) { + return null; + } + warnIfSinglePropertyTooLong("String", key, propertyArray.length); + return propertyArray[0]; + } + + /** + * Retrieves a {@code long} value by key. + * + * @param key The key to look for. + * @return The first {@code long} associated with the given key or default value {@code 0} if + * there is no such key or the value is of a different type. + */ + public long getPropertyLong(@NonNull String key) { + Preconditions.checkNotNull(key); + long[] propertyArray = getPropertyLongArray(key); + if (propertyArray == null || propertyArray.length == 0) { + return 0; + } + warnIfSinglePropertyTooLong("Long", key, propertyArray.length); + return propertyArray[0]; + } + + /** + * Retrieves a {@code double} value by key. + * + * @param key The key to look for. + * @return The first {@code double} associated with the given key or default value {@code 0.0} + * if there is no such key or the value is of a different type. + */ + public double getPropertyDouble(@NonNull String key) { + Preconditions.checkNotNull(key); + double[] propertyArray = getPropertyDoubleArray(key); + if (propertyArray == null || propertyArray.length == 0) { + return 0.0; + } + warnIfSinglePropertyTooLong("Double", key, propertyArray.length); + return propertyArray[0]; + } + + /** + * Retrieves a {@code boolean} value by key. + * + * @param key The key to look for. + * @return The first {@code boolean} associated with the given key or default value + * {@code false} if there is no such key or the value is of a different type. + */ + public boolean getPropertyBoolean(@NonNull String key) { + Preconditions.checkNotNull(key); + boolean[] propertyArray = getPropertyBooleanArray(key); + if (propertyArray == null || propertyArray.length == 0) { + return false; + } + warnIfSinglePropertyTooLong("Boolean", key, propertyArray.length); + return propertyArray[0]; + } + + /** + * Retrieves a {@code byte[]} value by key. + * + * @param key The key to look for. + * @return The first {@code byte[]} associated with the given key or {@code null} if there + * is no such key or the value is of a different type. + */ + @Nullable + public byte[] getPropertyBytes(@NonNull String key) { + Preconditions.checkNotNull(key); + byte[][] propertyArray = getPropertyBytesArray(key); + if (propertyArray == null || propertyArray.length == 0) { + return null; + } + warnIfSinglePropertyTooLong("ByteArray", key, propertyArray.length); + return propertyArray[0]; + } + + /** + * Retrieves a {@link GenericDocument} value by key. + * + * @param key The key to look for. + * @return The first {@link GenericDocument} associated with the given key or {@code null} if + * there is no such key or the value is of a different type. + */ + @Nullable + public GenericDocument getPropertyDocument(@NonNull String key) { + Preconditions.checkNotNull(key); + GenericDocument[] propertyArray = getPropertyDocumentArray(key); + if (propertyArray == null || propertyArray.length == 0) { + return null; + } + warnIfSinglePropertyTooLong("Document", key, propertyArray.length); + return propertyArray[0]; + } + + /** Prints a warning to logcat if the given propertyLength is greater than 1. */ + private static void warnIfSinglePropertyTooLong( + @NonNull String propertyType, @NonNull String key, int propertyLength) { + if (propertyLength > 1) { + Log.w(TAG, "The value for \"" + key + "\" contains " + propertyLength + + " elements. Only the first one will be returned from " + + "getProperty" + propertyType + "(). Try getProperty" + propertyType + + "Array()."); + } + } + + /** + * Retrieves a repeated {@code String} property by key. + * + * @param key The key to look for. + * @return The {@code String[]} associated with the given key, or {@code null} if no value + * is set or the value is of a different type. + */ + @Nullable + public String[] getPropertyStringArray(@NonNull String key) { + Preconditions.checkNotNull(key); + return getAndCastPropertyArray(key, String[].class); + } + + /** + * Retrieves a repeated {@link String} property by key. + * + * @param key The key to look for. + * @return The {@code long[]} associated with the given key, or {@code null} if no value is + * set or the value is of a different type. + */ + @Nullable + public long[] getPropertyLongArray(@NonNull String key) { + Preconditions.checkNotNull(key); + return getAndCastPropertyArray(key, long[].class); + } + + /** + * Retrieves a repeated {@code double} property by key. + * + * @param key The key to look for. + * @return The {@code double[]} associated with the given key, or {@code null} if no value + * is set or the value is of a different type. + */ + @Nullable + public double[] getPropertyDoubleArray(@NonNull String key) { + Preconditions.checkNotNull(key); + return getAndCastPropertyArray(key, double[].class); + } + + /** + * Retrieves a repeated {@code boolean} property by key. + * + * @param key The key to look for. + * @return The {@code boolean[]} associated with the given key, or {@code null} if no value + * is set or the value is of a different type. + */ + @Nullable + public boolean[] getPropertyBooleanArray(@NonNull String key) { + Preconditions.checkNotNull(key); + return getAndCastPropertyArray(key, boolean[].class); + } + + /** + * Retrieves a {@code byte[][]} property by key. + * + * @param key The key to look for. + * @return The {@code byte[][]} associated with the given key, or {@code null} if no value + * is set or the value is of a different type. + */ + @SuppressLint("ArrayReturn") + @Nullable + @SuppressWarnings("unchecked") + public byte[][] getPropertyBytesArray(@NonNull String key) { + Preconditions.checkNotNull(key); + ArrayList<Bundle> bundles = getAndCastPropertyArray(key, ArrayList.class); + if (bundles == null || bundles.size() == 0) { + return null; + } + byte[][] bytes = new byte[bundles.size()][]; + for (int i = 0; i < bundles.size(); i++) { + Bundle bundle = bundles.get(i); + if (bundle == null) { + Log.e(TAG, "The inner bundle is null at " + i + ", for key: " + key); + continue; + } + byte[] innerBytes = bundle.getByteArray(BYTE_ARRAY_FIELD); + if (innerBytes == null) { + Log.e(TAG, "The bundle at " + i + " contains a null byte[]."); + continue; + } + bytes[i] = innerBytes; + } + return bytes; + } + + /** + * Retrieves a repeated {@link GenericDocument} property by key. + * + * @param key The key to look for. + * @return The {@link GenericDocument}[] associated with the given key, or {@code null} if no + * value is set or the value is of a different type. + */ + @SuppressLint("ArrayReturn") + @Nullable + public GenericDocument[] getPropertyDocumentArray(@NonNull String key) { + Preconditions.checkNotNull(key); + Bundle[] bundles = getAndCastPropertyArray(key, Bundle[].class); + if (bundles == null || bundles.length == 0) { + return null; + } + GenericDocument[] documents = new GenericDocument[bundles.length]; + for (int i = 0; i < bundles.length; i++) { + if (bundles[i] == null) { + Log.e(TAG, "The inner bundle is null at " + i + ", for key: " + key); + continue; + } + documents[i] = new GenericDocument(bundles[i]); + } + return documents; + } + + /** + * Gets a repeated property of the given key, and casts it to the given class type, which + * must be an array class type. + */ + @Nullable + private <T> T getAndCastPropertyArray(@NonNull String key, @NonNull Class<T> tClass) { + Object value = mProperties.get(key); + if (value == null) { + return null; + } + try { + return tClass.cast(value); + } catch (ClassCastException e) { + Log.w(TAG, "Error casting to requested type for key \"" + key + "\"", e); + return null; + } + } + + @Override + public boolean equals(@Nullable Object other) { + // Check only proto's equality is sufficient here since all properties in + // mProperties are ordered by keys and stored in proto. + if (this == other) { + return true; + } + if (!(other instanceof GenericDocument)) { + return false; + } + GenericDocument otherDocument = (GenericDocument) other; + return bundleEquals(this.mBundle, otherDocument.mBundle); + } + + /** + * Deeply checks two bundle is equally or not. + * <p> Two bundle will be considered equally if they contains same content. + */ + @SuppressWarnings("unchecked") + private static boolean bundleEquals(Bundle one, Bundle two) { + if (one.size() != two.size()) { + return false; + } + Set<String> keySetOne = one.keySet(); + Object valueOne; + Object valueTwo; + // Bundle inherit its equals() from Object.java, which only compare their memory address. + // We should iterate all keys and check their presents and values in both bundle. + for (String key : keySetOne) { + valueOne = one.get(key); + valueTwo = two.get(key); + if (valueOne instanceof Bundle + && valueTwo instanceof Bundle + && !bundleEquals((Bundle) valueOne, (Bundle) valueTwo)) { + return false; + } else if (valueOne == null && (valueTwo != null || !two.containsKey(key))) { + // If we call bundle.get(key) when the 'key' doesn't actually exist in the + // bundle, we'll get back a null. So make sure that both values are null and + // both keys exist in the bundle. + return false; + } else if (valueOne instanceof boolean[]) { + if (!(valueTwo instanceof boolean[]) + || !Arrays.equals((boolean[]) valueOne, (boolean[]) valueTwo)) { + return false; + } + } else if (valueOne instanceof long[]) { + if (!(valueTwo instanceof long[]) + || !Arrays.equals((long[]) valueOne, (long[]) valueTwo)) { + return false; + } + } else if (valueOne instanceof double[]) { + if (!(valueTwo instanceof double[]) + || !Arrays.equals((double[]) valueOne, (double[]) valueTwo)) { + return false; + } + } else if (valueOne instanceof Bundle[]) { + if (!(valueTwo instanceof Bundle[])) { + return false; + } + Bundle[] bundlesOne = (Bundle[]) valueOne; + Bundle[] bundlesTwo = (Bundle[]) valueTwo; + if (bundlesOne.length != bundlesTwo.length) { + return false; + } + for (int i = 0; i < bundlesOne.length; i++) { + if (!bundleEquals(bundlesOne[i], bundlesTwo[i])) { + return false; + } + } + } else if (valueOne instanceof ArrayList) { + if (!(valueTwo instanceof ArrayList)) { + return false; + } + ArrayList<Bundle> bundlesOne = (ArrayList<Bundle>) valueOne; + ArrayList<Bundle> bundlesTwo = (ArrayList<Bundle>) valueTwo; + if (bundlesOne.size() != bundlesTwo.size()) { + return false; + } + for (int i = 0; i < bundlesOne.size(); i++) { + if (!bundleEquals(bundlesOne.get(i), bundlesTwo.get(i))) { + return false; + } + } + } else if (valueOne instanceof Object[]) { + if (!(valueTwo instanceof Object[]) + || !Arrays.equals((Object[]) valueOne, (Object[]) valueTwo)) { + return false; + } + } + } + return true; + } + + @Override + public int hashCode() { + if (mHashCode == null) { + mHashCode = bundleHashCode(mBundle); + } + return mHashCode; + } + + /** + * Calculates the hash code for a bundle. + * <p> The hash code is only effected by the content in the bundle. Bundles will get + * consistent hash code if they have same content. + */ + @SuppressWarnings("unchecked") + private static int bundleHashCode(Bundle bundle) { + int[] hashCodes = new int[bundle.size()]; + int i = 0; + // Bundle inherit its hashCode() from Object.java, which only relative to their memory + // address. Bundle doesn't have an order, so we should iterate all keys and combine + // their value's hashcode into an array. And use the hashcode of the array to be + // the hashcode of the bundle. + for (String key : bundle.keySet()) { + Object value = bundle.get(key); + if (value instanceof boolean[]) { + hashCodes[i++] = Arrays.hashCode((boolean[]) value); + } else if (value instanceof long[]) { + hashCodes[i++] = Arrays.hashCode((long[]) value); + } else if (value instanceof double[]) { + hashCodes[i++] = Arrays.hashCode((double[]) value); + } else if (value instanceof String[]) { + hashCodes[i++] = Arrays.hashCode((Object[]) value); + } else if (value instanceof Bundle) { + hashCodes[i++] = bundleHashCode((Bundle) value); + } else if (value instanceof Bundle[]) { + Bundle[] bundles = (Bundle[]) value; + int[] innerHashCodes = new int[bundles.length]; + for (int j = 0; j < innerHashCodes.length; j++) { + innerHashCodes[j] = bundleHashCode(bundles[j]); + } + hashCodes[i++] = Arrays.hashCode(innerHashCodes); + } else if (value instanceof ArrayList) { + ArrayList<Bundle> bundles = (ArrayList<Bundle>) value; + int[] innerHashCodes = new int[bundles.size()]; + for (int j = 0; j < innerHashCodes.length; j++) { + innerHashCodes[j] = bundleHashCode(bundles.get(j)); + } + hashCodes[i++] = Arrays.hashCode(innerHashCodes); + } else { + hashCodes[i++] = value.hashCode(); + } + } + return Arrays.hashCode(hashCodes); + } + + @Override + @NonNull + public String toString() { + return bundleToString(mBundle).toString(); + } + + @SuppressWarnings("unchecked") + private static StringBuilder bundleToString(Bundle bundle) { + StringBuilder stringBuilder = new StringBuilder(); + try { + final Set<String> keySet = bundle.keySet(); + String[] keys = keySet.toArray(new String[0]); + // Sort keys to make output deterministic. We need a custom comparator to handle + // nulls (arbitrarily putting them first, similar to Comparator.nullsFirst, which is + // only available since N). + Arrays.sort( + keys, + (@Nullable String s1, @Nullable String s2) -> { + if (s1 == null) { + return s2 == null ? 0 : -1; + } else if (s2 == null) { + return 1; + } else { + return s1.compareTo(s2); + } + }); + for (String key : keys) { + stringBuilder.append("{ key: '").append(key).append("' value: "); + Object valueObject = bundle.get(key); + if (valueObject == null) { + stringBuilder.append("<null>"); + } else if (valueObject instanceof Bundle) { + stringBuilder.append(bundleToString((Bundle) valueObject)); + } else if (valueObject.getClass().isArray()) { + stringBuilder.append("[ "); + for (int i = 0; i < Array.getLength(valueObject); i++) { + Object element = Array.get(valueObject, i); + stringBuilder.append("'"); + if (element instanceof Bundle) { + stringBuilder.append(bundleToString((Bundle) element)); + } else { + stringBuilder.append(Array.get(valueObject, i)); + } + stringBuilder.append("' "); + } + stringBuilder.append("]"); + } else if (valueObject instanceof ArrayList) { + for (Bundle innerBundle : (ArrayList<Bundle>) valueObject) { + stringBuilder.append(bundleToString(innerBundle)); + } + } else { + stringBuilder.append(valueObject.toString()); + } + stringBuilder.append(" } "); + } + } catch (RuntimeException e) { + // Catch any exceptions here since corrupt Bundles can throw different types of + // exceptions (e.g. b/38445840 & b/68937025). + stringBuilder.append("<error>"); + } + return stringBuilder; + } + + /** + * The builder class for {@link GenericDocument}. + * + * @param <BuilderType> Type of subclass who extend this. + */ + public static class Builder<BuilderType extends Builder> { + + private final Bundle mProperties = new Bundle(); + private final Bundle mBundle = new Bundle(); + private final BuilderType mBuilderTypeInstance; + private boolean mBuilt = false; + + /** + * Create a new {@link GenericDocument.Builder}. + * + * @param uri The uri of {@link GenericDocument}. + * @param schemaType The schema type of the {@link GenericDocument}. The passed-in + * {@code schemaType} must be defined using {@code AppSearchManager#setSchema} prior + * to inserting a document of this {@code schemaType} into the AppSearch index using + * {@code AppSearchManager#putDocuments}. Otherwise, the document will be + * rejected by {@code AppSearchManager#putDocuments}. + */ + //TODO(b/157082794) Linkify AppSearchManager once that API is public. + @SuppressWarnings("unchecked") + public Builder(@NonNull String uri, @NonNull String schemaType) { + Preconditions.checkNotNull(uri); + Preconditions.checkNotNull(schemaType); + mBuilderTypeInstance = (BuilderType) this; + mBundle.putString(GenericDocument.URI_FIELD, uri); + mBundle.putString(GenericDocument.SCHEMA_TYPE_FIELD, schemaType); + mBundle.putString(GenericDocument.NAMESPACE_FIELD, DEFAULT_NAMESPACE); + // Set current timestamp for creation timestamp by default. + mBundle.putLong(GenericDocument.CREATION_TIMESTAMP_MILLIS_FIELD, + System.currentTimeMillis()); + mBundle.putLong(GenericDocument.TTL_MILLIS_FIELD, DEFAULT_TTL_MILLIS); + mBundle.putInt(GenericDocument.SCORE_FIELD, DEFAULT_SCORE); + mBundle.putBundle(PROPERTIES_FIELD, mProperties); + } + + /** + * Set the app-defined namespace this Document resides in. No special values are + * reserved or understood by the infrastructure. URIs are unique within a namespace. The + * number of namespaces per app should be kept small for efficiency reasons. + */ + @NonNull + public BuilderType setNamespace(@NonNull String namespace) { + mBundle.putString(GenericDocument.NAMESPACE_FIELD, namespace); + return mBuilderTypeInstance; + } + + /** + * Sets the score of the {@link GenericDocument}. + * + * <p>The score is a query-independent measure of the document's quality, relative to + * other {@link GenericDocument}s of the same type. + * + * @throws IllegalArgumentException If the provided value is negative. + */ + @NonNull + public BuilderType setScore(@IntRange(from = 0, to = Integer.MAX_VALUE) int score) { + Preconditions.checkState(!mBuilt, "Builder has already been used"); + if (score < 0) { + throw new IllegalArgumentException("Document score cannot be negative."); + } + mBundle.putInt(GenericDocument.SCORE_FIELD, score); + return mBuilderTypeInstance; + } + + /** + * Set the creation timestamp in milliseconds of the {@link GenericDocument}. Should be + * set using a value obtained from the {@link System#currentTimeMillis()} time base. + */ + @NonNull + public BuilderType setCreationTimestampMillis(long creationTimestampMillis) { + Preconditions.checkState(!mBuilt, "Builder has already been used"); + mBundle.putLong(GenericDocument.CREATION_TIMESTAMP_MILLIS_FIELD, + creationTimestampMillis); + return mBuilderTypeInstance; + } + + /** + * Set the TTL (Time To Live) of the {@link GenericDocument}, in milliseconds. + * + * <p>After this many milliseconds since the {@link #setCreationTimestampMillis creation + * timestamp}, the document is deleted. + * + * @param ttlMillis A non-negative duration in milliseconds. + * @throws IllegalArgumentException If the provided value is negative. + */ + @NonNull + public BuilderType setTtlMillis(long ttlMillis) { + Preconditions.checkState(!mBuilt, "Builder has already been used"); + if (ttlMillis < 0) { + throw new IllegalArgumentException("Document ttlMillis cannot be negative."); + } + mBundle.putLong(GenericDocument.TTL_MILLIS_FIELD, ttlMillis); + return mBuilderTypeInstance; + } + + /** + * Sets one or multiple {@code String} values for a property, replacing its previous + * values. + * + * @param key The key associated with the {@code values}. + * @param values The {@code String} values of the property. + */ + @NonNull + public BuilderType setProperty(@NonNull String key, @NonNull String... values) { + Preconditions.checkState(!mBuilt, "Builder has already been used"); + Preconditions.checkNotNull(key); + Preconditions.checkNotNull(values); + putInPropertyBundle(key, values); + return mBuilderTypeInstance; + } + + /** + * Sets one or multiple {@code boolean} values for a property, replacing its previous + * values. + * + * @param key The key associated with the {@code values}. + * @param values The {@code boolean} values of the property. + */ + @NonNull + public BuilderType setProperty(@NonNull String key, @NonNull boolean... values) { + Preconditions.checkState(!mBuilt, "Builder has already been used"); + Preconditions.checkNotNull(key); + Preconditions.checkNotNull(values); + putInPropertyBundle(key, values); + return mBuilderTypeInstance; + } + + /** + * Sets one or multiple {@code long} values for a property, replacing its previous + * values. + * + * @param key The key associated with the {@code values}. + * @param values The {@code long} values of the property. + */ + @NonNull + public BuilderType setProperty(@NonNull String key, @NonNull long... values) { + Preconditions.checkState(!mBuilt, "Builder has already been used"); + Preconditions.checkNotNull(key); + Preconditions.checkNotNull(values); + putInPropertyBundle(key, values); + return mBuilderTypeInstance; + } + + /** + * Sets one or multiple {@code double} values for a property, replacing its previous + * values. + * + * @param key The key associated with the {@code values}. + * @param values The {@code double} values of the property. + */ + @NonNull + public BuilderType setProperty(@NonNull String key, @NonNull double... values) { + Preconditions.checkState(!mBuilt, "Builder has already been used"); + Preconditions.checkNotNull(key); + Preconditions.checkNotNull(values); + putInPropertyBundle(key, values); + return mBuilderTypeInstance; + } + + /** + * Sets one or multiple {@code byte[]} for a property, replacing its previous values. + * + * @param key The key associated with the {@code values}. + * @param values The {@code byte[]} of the property. + */ + @NonNull + public BuilderType setProperty(@NonNull String key, @NonNull byte[]... values) { + Preconditions.checkState(!mBuilt, "Builder has already been used"); + Preconditions.checkNotNull(key); + Preconditions.checkNotNull(values); + putInPropertyBundle(key, values); + return mBuilderTypeInstance; + } + + /** + * Sets one or multiple {@link GenericDocument} values for a property, replacing its + * previous values. + * + * @param key The key associated with the {@code values}. + * @param values The {@link GenericDocument} values of the property. + */ + @NonNull + public BuilderType setProperty(@NonNull String key, @NonNull GenericDocument... values) { + Preconditions.checkState(!mBuilt, "Builder has already been used"); + Preconditions.checkNotNull(key); + Preconditions.checkNotNull(values); + putInPropertyBundle(key, values); + return mBuilderTypeInstance; + } + + private void putInPropertyBundle(@NonNull String key, @NonNull String[] values) + throws IllegalArgumentException { + validateRepeatedPropertyLength(key, values.length); + for (int i = 0; i < values.length; i++) { + if (values[i] == null) { + throw new IllegalArgumentException("The String at " + i + " is null."); + } else if (values[i].length() > MAX_STRING_LENGTH) { + throw new IllegalArgumentException("The String at " + i + " length is: " + + values[i].length() + ", which exceeds length limit: " + + MAX_STRING_LENGTH + "."); + } + } + mProperties.putStringArray(key, values); + } + + private void putInPropertyBundle(@NonNull String key, @NonNull boolean[] values) { + validateRepeatedPropertyLength(key, values.length); + mProperties.putBooleanArray(key, values); + } + + private void putInPropertyBundle(@NonNull String key, @NonNull double[] values) { + validateRepeatedPropertyLength(key, values.length); + mProperties.putDoubleArray(key, values); + } + + private void putInPropertyBundle(@NonNull String key, @NonNull long[] values) { + validateRepeatedPropertyLength(key, values.length); + mProperties.putLongArray(key, values); + } + + /** + * Converts and saves a byte[][] into {@link #mProperties}. + * + * <p>Bundle doesn't support for two dimension array byte[][], we are converting byte[][] + * into ArrayList<Bundle>, and each elements will contain a one dimension byte[]. + */ + private void putInPropertyBundle(@NonNull String key, @NonNull byte[][] values) { + validateRepeatedPropertyLength(key, values.length); + ArrayList<Bundle> bundles = new ArrayList<>(values.length); + for (int i = 0; i < values.length; i++) { + if (values[i] == null) { + throw new IllegalArgumentException("The byte[] at " + i + " is null."); + } + Bundle bundle = new Bundle(); + bundle.putByteArray(BYTE_ARRAY_FIELD, values[i]); + bundles.add(bundle); + } + mProperties.putParcelableArrayList(key, bundles); + } + + private void putInPropertyBundle(@NonNull String key, @NonNull GenericDocument[] values) { + validateRepeatedPropertyLength(key, values.length); + Bundle[] documentBundles = new Bundle[values.length]; + for (int i = 0; i < values.length; i++) { + if (values[i] == null) { + throw new IllegalArgumentException("The document at " + i + " is null."); + } + documentBundles[i] = values[i].mBundle; + } + mProperties.putParcelableArray(key, documentBundles); + } + + private static void validateRepeatedPropertyLength(@NonNull String key, int length) { + if (length == 0) { + throw new IllegalArgumentException("The input array is empty."); + } else if (length > MAX_REPEATED_PROPERTY_LENGTH) { + throw new IllegalArgumentException( + "Repeated property \"" + key + "\" has length " + length + + ", which exceeds the limit of " + + MAX_REPEATED_PROPERTY_LENGTH); + } + } + + /** Builds the {@link GenericDocument} object. */ + @NonNull + public GenericDocument build() { + Preconditions.checkState(!mBuilt, "Builder has already been used"); + mBuilt = true; + return new GenericDocument(mBundle); + } + } +} diff --git a/apex/appsearch/framework/java/android/app/appsearch/GetByUriRequest.java b/apex/appsearch/framework/java/android/app/appsearch/GetByUriRequest.java new file mode 100644 index 000000000000..3c0e746f92ab --- /dev/null +++ b/apex/appsearch/framework/java/android/app/appsearch/GetByUriRequest.java @@ -0,0 +1,100 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app.appsearch; + +import android.annotation.NonNull; + +import android.util.ArraySet; +import com.android.internal.util.Preconditions; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Set; + +/** + * Encapsulates a request to retrieve documents by namespace and URI. + * + * @see AppSearchManager#getByUri + * @hide + */ +public final class GetByUriRequest { + private final String mNamespace; + private final Set<String> mUris; + + GetByUriRequest(@NonNull String namespace, @NonNull Set<String> uris) { + mNamespace = namespace; + mUris = uris; + } + + /** @hide */ + + @NonNull + public String getNamespace() { + return mNamespace; + } + + /** @hide */ + + @NonNull + public Set<String> getUris() { + return mUris; + } + + /** Builder for {@link GetByUriRequest} objects. */ + public static final class Builder { + private String mNamespace = GenericDocument.DEFAULT_NAMESPACE; + private final Set<String> mUris = new ArraySet<>(); + private boolean mBuilt = false; + + /** + * Sets which namespace these documents will be retrieved from. + * + * <p>If this is not set, it defaults to {@link GenericDocument#DEFAULT_NAMESPACE}. + */ + @NonNull + public Builder setNamespace(@NonNull String namespace) { + Preconditions.checkState(!mBuilt, "Builder has already been used"); + Preconditions.checkNotNull(namespace); + mNamespace = namespace; + return this; + } + + /** Adds one or more URIs to the request. */ + @NonNull + public Builder addUris(@NonNull String... uris) { + Preconditions.checkNotNull(uris); + return addUris(Arrays.asList(uris)); + } + + /** Adds one or more URIs to the request. */ + @NonNull + public Builder addUris(@NonNull Collection<String> uris) { + Preconditions.checkState(!mBuilt, "Builder has already been used"); + Preconditions.checkNotNull(uris); + mUris.addAll(uris); + return this; + } + + /** Builds a new {@link GetByUriRequest}. */ + @NonNull + public GetByUriRequest build() { + Preconditions.checkState(!mBuilt, "Builder has already been used"); + mBuilt = true; + return new GetByUriRequest(mNamespace, mUris); + } + } +} diff --git a/apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl b/apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl index c710a29919e3..352a980eef27 100644 --- a/apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl +++ b/apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl @@ -21,12 +21,14 @@ import com.android.internal.infra.AndroidFuture; parcelable AppSearchResult; parcelable AppSearchBatchResult; +parcelable SearchResults; /** {@hide} */ interface IAppSearchManager { /** * Sets the schema. * + * @param databaseName The databaseName this document resides in. * @param schemaBundles List of AppSearchSchema bundles. * @param forceOverride Whether to apply the new schema even if it is incompatible. All * incompatible documents will be deleted. @@ -34,6 +36,7 @@ interface IAppSearchManager { * The results of the call. */ void setSchema( + in String databaseName, in List<Bundle> schemaBundles, boolean forceOverride, in AndroidFuture<AppSearchResult> callback); @@ -41,7 +44,8 @@ interface IAppSearchManager { /** * Inserts documents into the index. * - * @param documentsBytes {@link List}<byte[]> of serialized DocumentProtos. + * @param databaseName The name of the database where this document lives. + * @param documentBundes List of GenericDocument bundles. * @param callback * {@link AndroidFuture}<{@link AppSearchBatchResult}<{@link String}, {@link Void}>>. * If the call fails to start, {@code callback} will be completed exceptionally. Otherwise, @@ -49,30 +53,40 @@ interface IAppSearchManager { * {@link AppSearchBatchResult}<{@link String}, {@link Void}> * where the keys are document URIs, and the values are {@code null}. */ - void putDocuments(in List documentsBytes, in AndroidFuture<AppSearchBatchResult> callback); + void putDocuments( + in String databaseName, + in List<Bundle> documentBundles, + in AndroidFuture<AppSearchBatchResult> callback); /** * Retrieves documents from the index. * + * @param databaseName The databaseName this document resides in. + * @param namespace The namespace this document resides in. * @param uris The URIs of the documents to retrieve * @param callback - * {@link AndroidFuture}<{@link AppSearchBatchResult}<{@link String}, {@link byte[]}>>. + * {@link AndroidFuture}<{@link AppSearchBatchResult}<{@link String}, {@link Bundle}>>. * If the call fails to start, {@code callback} will be completed exceptionally. Otherwise, * {@code callback} will be completed with an - * {@link AppSearchBatchResult}<{@link String}, {@link byte[]}> - * where the keys are document URIs, and the values are serialized Document protos. + * {@link AppSearchBatchResult}<{@link String}, {@link Bundle}> + * where the keys are document URIs, and the values are Document bundles. */ - void getDocuments(in List<String> uris, in AndroidFuture<AppSearchBatchResult> callback); + void getDocuments( + in String databaseName, + in String namespace, + in List<String> uris, + in AndroidFuture<AppSearchBatchResult> callback); /** * Searches a document based on a given specifications. * + * @param databaseName The databaseName this query for. * @param queryExpression String to search for * @param searchSpecBundle SearchSpec bundle - * @param callback {@link AndroidFuture}<{@link AppSearchResult}<{@link byte[]}>> - * Will be completed with a serialized {@link SearchResultsProto}. + * @param callback {@link AndroidFuture}<{@link AppSearchResult}<{@link SearchResults}>> */ void query( + in String databaseName, in String queryExpression, in Bundle searchSpecBundle, in AndroidFuture<AppSearchResult> callback); @@ -80,6 +94,8 @@ interface IAppSearchManager { /** * Deletes documents by URI. * + * @param databaseName The databaseName the document is in. + * @param namespace Namespace of the document to remove. * @param uris The URIs of the documents to delete * @param callback * {@link AndroidFuture}<{@link AppSearchBatchResult}<{@link String}, {@link Void}>>. @@ -89,11 +105,16 @@ interface IAppSearchManager { * where the keys are document URIs. If a document doesn't exist, it will be reported as a * failure where the {@code throwable} is {@code null}. */ - void delete(in List<String> uris, in AndroidFuture<AppSearchBatchResult> callback); + void delete( + in String databaseName, + in String namespace, + in List<String> uris, + in AndroidFuture<AppSearchBatchResult> callback); /** * Deletes documents by schema type. * + * @param databaseName The databaseName the document is in. * @param schemaTypes The schema types of the documents to delete * @param callback * {@link AndroidFuture}<{@link AppSearchBatchResult}<{@link String}, {@link Void}>>. @@ -104,13 +125,16 @@ interface IAppSearchManager { * failure where the {@code throwable} is {@code null}. */ void deleteByTypes( - in List<String> schemaTypes, in AndroidFuture<AppSearchBatchResult> callback); + in String databaseName, + in List<String> schemaTypes, + in AndroidFuture<AppSearchBatchResult> callback); /** * Deletes all documents belonging to the calling app. * + * @param databaseName The databaseName to remove all documents from. * @param callback {@link AndroidFuture}<{@link AppSearchResult}<{@link Void}>>. * Will be completed with the result of the call. */ - void deleteAll(in AndroidFuture<AppSearchResult> callback); + void deleteAll(in String databaseName, in AndroidFuture<AppSearchResult> callback); } diff --git a/apex/appsearch/framework/java/android/app/appsearch/MatchInfo.java b/apex/appsearch/framework/java/android/app/appsearch/MatchInfo.java deleted file mode 100644 index 5ce296082d70..000000000000 --- a/apex/appsearch/framework/java/android/app/appsearch/MatchInfo.java +++ /dev/null @@ -1,182 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.app.appsearch; - -import android.annotation.NonNull; -import android.util.Range; - -import com.google.android.icing.proto.SnippetMatchProto; - -/** - * Snippet: It refers to a substring of text from the content of document that is returned as a - * part of search result. - * This class represents a match objects for any Snippets that might be present in - * {@link SearchResults} from query. Using this class user can get the full text, exact matches and - * Snippets of document content for a given match. - * - * <p>Class Example 1: - * A document contains following text in property subject: - * <p>A commonly used fake word is foo. Another nonsense word that’s used a lot is bar. - * - * <p>If the queryExpression is "foo". - * - * <p>{@link MatchInfo#getPropertyPath()} returns "subject" - * <p>{@link MatchInfo#getFullText()} returns "A commonly used fake word is foo. Another nonsense - * word that’s used a lot is bar." - * <p>{@link MatchInfo#getExactMatchPosition()} returns [29, 32] - * <p>{@link MatchInfo#getExactMatch()} returns "foo" - * <p>{@link MatchInfo#getSnippetPosition()} returns [29, 41] - * <p>{@link MatchInfo#getSnippet()} returns "is foo. Another" - * <p> - * <p>Class Example 2: - * A document contains a property name sender which contains 2 property names name and email, so - * we will have 2 property paths: {@code sender.name} and {@code sender.email}. - * <p> Let {@code sender.name = "Test Name Jr."} and {@code sender.email = "TestNameJr@gmail.com"} - * - * <p>If the queryExpression is "Test". We will have 2 matches. - * - * <p> Match-1 - * <p>{@link MatchInfo#getPropertyPath()} returns "sender.name" - * <p>{@link MatchInfo#getFullText()} returns "Test Name Jr." - * <p>{@link MatchInfo#getExactMatchPosition()} returns [0, 4] - * <p>{@link MatchInfo#getExactMatch()} returns "Test" - * <p>{@link MatchInfo#getSnippetPosition()} returns [0, 9] - * <p>{@link MatchInfo#getSnippet()} returns "Test Name Jr." - * <p> Match-2 - * <p>{@link MatchInfo#getPropertyPath()} returns "sender.email" - * <p>{@link MatchInfo#getFullText()} returns "TestNameJr@gmail.com" - * <p>{@link MatchInfo#getExactMatchPosition()} returns [0, 20] - * <p>{@link MatchInfo#getExactMatch()} returns "TestNameJr@gmail.com" - * <p>{@link MatchInfo#getSnippetPosition()} returns [0, 20] - * <p>{@link MatchInfo#getSnippet()} returns "TestNameJr@gmail.com" - * @hide - */ -// TODO(sidchhabra): Capture real snippet after integration with icingLib. -public final class MatchInfo { - - private final String mPropertyPath; - private final SnippetMatchProto mSnippetMatch; - private final AppSearchDocument mDocument; - /** - * List of content with same property path in a document when there are multiple matches in - * repeated sections. - */ - private final String[] mValues; - - /** @hide */ - public MatchInfo(@NonNull String propertyPath, @NonNull SnippetMatchProto snippetMatch, - @NonNull AppSearchDocument document) { - mPropertyPath = propertyPath; - mSnippetMatch = snippetMatch; - mDocument = document; - // In IcingLib snippeting is available for only 3 data types i.e String, double and long, - // so we need to check which of these three are requested. - // TODO (sidchhabra): getPropertyStringArray takes property name, handle for property path. - String[] values = mDocument.getPropertyStringArray(propertyPath); - if (values == null) { - values = doubleToString(mDocument.getPropertyDoubleArray(propertyPath)); - } - if (values == null) { - values = longToString(mDocument.getPropertyLongArray(propertyPath)); - } - if (values == null) { - throw new IllegalStateException("No content found for requested property path!"); - } - mValues = values; - } - - /** - * Gets the property path corresponding to the given entry. - * <p>Property Path: '.' - delimited sequence of property names indicating which property in - * the Document these snippets correspond to. - * <p>Example properties: 'body', 'sender.name', 'sender.emailaddress', etc. - * For class example 1 this returns "subject" - */ - @NonNull - public String getPropertyPath() { - return mPropertyPath; - } - - /** - * Gets the full text corresponding to the given entry. - * <p>For class example this returns "A commonly used fake word is foo. Another nonsense word - * that’s used a lot is bar." - */ - @NonNull - public String getFullText() { - return mValues[mSnippetMatch.getValuesIndex()]; - } - - /** - * Gets the exact match range corresponding to the given entry. - * <p>For class example 1 this returns [29, 32] - */ - @NonNull - public Range getExactMatchPosition() { - return new Range(mSnippetMatch.getExactMatchPosition(), - mSnippetMatch.getExactMatchPosition() + mSnippetMatch.getExactMatchBytes()); - } - - /** - * Gets the exact match corresponding to the given entry. - * <p>For class example 1 this returns "foo" - */ - @NonNull - public CharSequence getExactMatch() { - return getSubstring(getExactMatchPosition()); - } - - /** - * Gets the snippet range corresponding to the given entry. - * <p>For class example 1 this returns [29, 41] - */ - @NonNull - public Range getSnippetPosition() { - return new Range(mSnippetMatch.getWindowPosition(), - mSnippetMatch.getWindowPosition() + mSnippetMatch.getWindowBytes()); - } - - /** - * Gets the snippet corresponding to the given entry. - * <p>Snippet - Provides a subset of the content to display. The - * length of this content can be changed {@link SearchSpec.Builder#setMaxSnippetSize(int)}. - * Windowing is centered around the middle of the matched token with content on either side - * clipped to token boundaries. - * <p>For class example 1 this returns "foo. Another" - */ - @NonNull - public CharSequence getSnippet() { - return getSubstring(getSnippetPosition()); - } - - private CharSequence getSubstring(Range range) { - return getFullText() - .substring((int) range.getLower(), (int) range.getUpper()); - } - - /** Utility method to convert double[] to String[] */ - private String[] doubleToString(double[] values) { - //TODO(sidchhabra): Implement the method. - return null; - } - - /** Utility method to convert long[] to String[] */ - private String[] longToString(long[] values) { - //TODO(sidchhabra): Implement the method. - return null; - } -} diff --git a/apex/appsearch/framework/java/android/app/appsearch/PutDocumentsRequest.java b/apex/appsearch/framework/java/android/app/appsearch/PutDocumentsRequest.java new file mode 100644 index 000000000000..7e97542c6f02 --- /dev/null +++ b/apex/appsearch/framework/java/android/app/appsearch/PutDocumentsRequest.java @@ -0,0 +1,77 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app.appsearch; + +import android.annotation.NonNull; + +import com.android.internal.util.Preconditions; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; + +/** + * Encapsulates a request to index a document into an {@link AppSearchManager} database. + * + * @see AppSearchManager#putDocuments + * @hide + */ +public final class PutDocumentsRequest { + private final List<GenericDocument> mDocuments; + + PutDocumentsRequest(List<GenericDocument> documents) { + mDocuments = documents; + } + + /** @hide */ + + @NonNull + public List<GenericDocument> getDocuments() { + return mDocuments; + } + + /** Builder for {@link PutDocumentsRequest} objects. */ + public static final class Builder { + private final List<GenericDocument> mDocuments = new ArrayList<>(); + private boolean mBuilt = false; + + /** Adds one or more documents to the request. */ + @NonNull + public Builder addGenericDocument(@NonNull GenericDocument... documents) { + Preconditions.checkNotNull(documents); + return addGenericDocument(Arrays.asList(documents)); + } + + /** Adds one or more documents to the request. */ + @NonNull + public Builder addGenericDocument(@NonNull Collection<GenericDocument> documents) { + Preconditions.checkState(!mBuilt, "Builder has already been used"); + Preconditions.checkNotNull(documents); + mDocuments.addAll(documents); + return this; + } + + /** Builds a new {@link PutDocumentsRequest}. */ + @NonNull + public PutDocumentsRequest build() { + Preconditions.checkState(!mBuilt, "Builder has already been used"); + mBuilt = true; + return new PutDocumentsRequest(mDocuments); + } + } +} diff --git a/apex/appsearch/framework/java/android/app/appsearch/RemoveByUriRequest.java b/apex/appsearch/framework/java/android/app/appsearch/RemoveByUriRequest.java new file mode 100644 index 000000000000..a047041a3082 --- /dev/null +++ b/apex/appsearch/framework/java/android/app/appsearch/RemoveByUriRequest.java @@ -0,0 +1,100 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app.appsearch; + +import android.annotation.NonNull; + +import android.util.ArraySet; +import com.android.internal.util.Preconditions; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Set; + +/** + * Encapsulates a request to remove documents by namespace and URI. + * + * @see AppSearchManager#removeByUri + * @hide + */ +public final class RemoveByUriRequest { + private final String mNamespace; + private final Set<String> mUris; + + RemoveByUriRequest(String namespace, Set<String> uris) { + mNamespace = namespace; + mUris = uris; + } + + /** @hide */ + + @NonNull + public String getNamespace() { + return mNamespace; + } + + /** @hide */ + + @NonNull + public Set<String> getUris() { + return mUris; + } + + /** Builder for {@link RemoveByUriRequest} objects. */ + public static final class Builder { + private String mNamespace = GenericDocument.DEFAULT_NAMESPACE; + private final Set<String> mUris = new ArraySet<>(); + private boolean mBuilt = false; + + /** + * Sets which namespace these documents will be removed from. + * + * <p>If this is not set, it defaults to {@link GenericDocument#DEFAULT_NAMESPACE}. + */ + @NonNull + public Builder setNamespace(@NonNull String namespace) { + Preconditions.checkState(!mBuilt, "Builder has already been used"); + Preconditions.checkNotNull(namespace); + mNamespace = namespace; + return this; + } + + /** Adds one or more URIs to the request. */ + @NonNull + public Builder addUris(@NonNull String... uris) { + Preconditions.checkNotNull(uris); + return addUris(Arrays.asList(uris)); + } + + /** Adds one or more URIs to the request. */ + @NonNull + public Builder addUris(@NonNull Collection<String> uris) { + Preconditions.checkState(!mBuilt, "Builder has already been used"); + Preconditions.checkNotNull(uris); + mUris.addAll(uris); + return this; + } + + /** Builds a new {@link RemoveByUriRequest}. */ + @NonNull + public RemoveByUriRequest build() { + Preconditions.checkState(!mBuilt, "Builder has already been used"); + mBuilt = true; + return new RemoveByUriRequest(mNamespace, mUris); + } + } +} diff --git a/apex/appsearch/framework/java/android/app/appsearch/SearchResult.java b/apex/appsearch/framework/java/android/app/appsearch/SearchResult.java new file mode 100644 index 000000000000..758280bbc322 --- /dev/null +++ b/apex/appsearch/framework/java/android/app/appsearch/SearchResult.java @@ -0,0 +1,368 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app.appsearch; + +import android.os.Bundle; + +import android.annotation.NonNull; +import android.annotation.Nullable; + +import java.util.Objects; +import com.android.internal.util.Preconditions; + +import java.util.ArrayList; +import java.util.List; + +/** + * This class represents one of the results obtained from the query. + * + * <p>It contains the document which matched, information about which section(s) in the document + * matched, and snippet information containing textual summaries of the document's match(es). + * @hide + */ +public final class SearchResult { + /** @hide */ + + public static final String DOCUMENT_FIELD = "document"; + + /** @hide */ + + public static final String MATCHES_FIELD = "matches"; + + @NonNull + private final Bundle mBundle; + + @NonNull + private final Bundle mDocumentBundle; + + @Nullable + private GenericDocument mDocument; + + @Nullable + private final List<Bundle> mMatchBundles; + + /** + * Contains a list of Snippets that matched the request. Only populated when requested in + * both {@link SearchSpec.Builder#setSnippetCount(int)} + * and {@link SearchSpec.Builder#setSnippetCountPerProperty(int)}. + * + * @see #getMatches() + */ + @Nullable + private List<MatchInfo> mMatches; + + /** @hide */ + + public SearchResult(@NonNull Bundle bundle) { + mBundle = Preconditions.checkNotNull(bundle); + mDocumentBundle = Preconditions.checkNotNull(bundle.getBundle(DOCUMENT_FIELD)); + mMatchBundles = bundle.getParcelableArrayList(MATCHES_FIELD); + } + + /** @hide */ + + @NonNull + public Bundle getBundle() { + return mBundle; + } + + /** + * Contains the matching {@link GenericDocument}. + * @return Document object which matched the query. + */ + @NonNull + public GenericDocument getDocument() { + if (mDocument == null) { + mDocument = new GenericDocument(mDocumentBundle); + } + return mDocument; + } + + /** + * Contains a list of Snippets that matched the request. Only populated when requested in + * both {@link SearchSpec.Builder#setSnippetCount(int)} + * and {@link SearchSpec.Builder#setSnippetCountPerProperty(int)}. + * + * @return List of matches based on {@link SearchSpec}, if snippeting is disabled and this + * method is called it will return {@code null}. Users can also restrict snippet population + * using {@link SearchSpec.Builder#setSnippetCount} and + * {@link SearchSpec.Builder#setSnippetCountPerProperty(int)}, for all results after that + * value this method will return {@code null}. + */ + @Nullable + public List<MatchInfo> getMatches() { + if (mMatchBundles != null && mMatches == null) { + mMatches = new ArrayList<>(mMatchBundles.size()); + for (int i = 0; i < mMatchBundles.size(); i++) { + MatchInfo matchInfo = new MatchInfo(getDocument(), mMatchBundles.get(i)); + mMatches.add(matchInfo); + } + } + return mMatches; + } + + /** + * Snippet: It refers to a substring of text from the content of document that is returned as a + * part of search result. + * This class represents a match objects for any Snippets that might be present in + * {@link SearchResults} from query. Using this class user can get the full text, exact matches + * and Snippets of document content for a given match. + * + * <p>Class Example 1: + * A document contains following text in property subject: + * <p>A commonly used fake word is foo. Another nonsense word that’s used a lot is bar. + * + * <p>If the queryExpression is "foo". + * + * <p>{@link MatchInfo#getPropertyPath()} returns "subject" + * <p>{@link MatchInfo#getFullText()} returns "A commonly used fake word is foo. Another + * nonsense word that’s used a lot is bar." + * <p>{@link MatchInfo#getExactMatchPosition()} returns [29, 32] + * <p>{@link MatchInfo#getExactMatch()} returns "foo" + * <p>{@link MatchInfo#getSnippetPosition()} returns [26, 33] + * <p>{@link MatchInfo#getSnippet()} returns "is foo." + * <p> + * <p>Class Example 2: + * A document contains a property name sender which contains 2 property names name and email, so + * we will have 2 property paths: {@code sender.name} and {@code sender.email}. + * <p>Let {@code sender.name = "Test Name Jr."} and + * {@code sender.email = "TestNameJr@gmail.com"} + * + * <p>If the queryExpression is "Test". We will have 2 matches. + * + * <p> Match-1 + * <p>{@link MatchInfo#getPropertyPath()} returns "sender.name" + * <p>{@link MatchInfo#getFullText()} returns "Test Name Jr." + * <p>{@link MatchInfo#getExactMatchPosition()} returns [0, 4] + * <p>{@link MatchInfo#getExactMatch()} returns "Test" + * <p>{@link MatchInfo#getSnippetPosition()} returns [0, 9] + * <p>{@link MatchInfo#getSnippet()} returns "Test Name" + * <p> Match-2 + * <p>{@link MatchInfo#getPropertyPath()} returns "sender.email" + * <p>{@link MatchInfo#getFullText()} returns "TestNameJr@gmail.com" + * <p>{@link MatchInfo#getExactMatchPosition()} returns [0, 20] + * <p>{@link MatchInfo#getExactMatch()} returns "TestNameJr@gmail.com" + * <p>{@link MatchInfo#getSnippetPosition()} returns [0, 20] + * <p>{@link MatchInfo#getSnippet()} returns "TestNameJr@gmail.com" + */ + public static final class MatchInfo { + /** + * The path of the matching snippet property. + * @hide + */ + + public static final String PROPERTY_PATH_FIELD = "propertyPath"; + + /** + * The index of matching value in its property. A property may have multiple values. This + * index indicates which value is the match. + * @hide + */ + + public static final String VALUES_INDEX_FIELD = "valuesIndex"; + + /** @hide */ + + public static final String EXACT_MATCH_POSITION_LOWER_FIELD = "exactMatchPositionLower"; + + /** @hide */ + + public static final String EXACT_MATCH_POSITION_UPPER_FIELD = "exactMatchPositionUpper"; + + /** @hide */ + + public static final String WINDOW_POSITION_LOWER_FIELD = "windowPositionLower"; + + /** @hide */ + + public static final String WINDOW_POSITION_UPPER_FIELD = "windowPositionUpper"; + + private final String mFullText; + private final String mPropertyPath; + private final Bundle mBundle; + private MatchRange mExactMatchRange; + private MatchRange mWindowRange; + + MatchInfo(@NonNull GenericDocument document, @NonNull Bundle bundle) { + mBundle = Preconditions.checkNotNull(bundle); + Preconditions.checkNotNull(document); + mPropertyPath = Preconditions.checkNotNull(bundle.getString(PROPERTY_PATH_FIELD)); + mFullText = getPropertyValues( + document, mPropertyPath, mBundle.getInt(VALUES_INDEX_FIELD)); + } + + /** + * Gets the property path corresponding to the given entry. + * <p>Property Path: '.' - delimited sequence of property names indicating which property in + * the Document these snippets correspond to. + * <p>Example properties: 'body', 'sender.name', 'sender.emailaddress', etc. + * For class example 1 this returns "subject" + */ + @NonNull + public String getPropertyPath() { + return mPropertyPath; + } + + /** + * Gets the full text corresponding to the given entry. + * <p>For class example this returns "A commonly used fake word is foo. Another nonsense + * word that's used a lot is bar." + */ + @NonNull + public String getFullText() { + return mFullText; + } + + /** + * Gets the exact {@link MatchRange} corresponding to the given entry. + * <p>For class example 1 this returns [29, 32] + */ + @NonNull + public MatchRange getExactMatchPosition() { + if (mExactMatchRange == null) { + mExactMatchRange = new MatchRange( + mBundle.getInt(EXACT_MATCH_POSITION_LOWER_FIELD), + mBundle.getInt(EXACT_MATCH_POSITION_UPPER_FIELD)); + } + return mExactMatchRange; + } + + /** + * Gets the {@link MatchRange} corresponding to the given entry. + * <p>For class example 1 this returns "foo" + */ + @NonNull + public CharSequence getExactMatch() { + return getSubstring(getExactMatchPosition()); + } + + /** + * Gets the snippet {@link MatchRange} corresponding to the given entry. + * <p>Only populated when set maxSnippetSize > 0 in + * {@link SearchSpec.Builder#setMaxSnippetSize}. + * <p>For class example 1 this returns [29, 41]. + */ + @NonNull + public MatchRange getSnippetPosition() { + if (mWindowRange == null) { + mWindowRange = new MatchRange( + mBundle.getInt(WINDOW_POSITION_LOWER_FIELD), + mBundle.getInt(WINDOW_POSITION_UPPER_FIELD)); + } + return mWindowRange; + } + + /** + * Gets the snippet corresponding to the given entry. + * <p>Snippet - Provides a subset of the content to display. Only populated when requested + * maxSnippetSize > 0. The size of this content can be changed by + * {@link SearchSpec.Builder#setMaxSnippetSize}. Windowing is centered around the middle of + * the matched token with content on either side clipped to token boundaries. + * <p>For class example 1 this returns "foo. Another" + */ + @NonNull + public CharSequence getSnippet() { + return getSubstring(getSnippetPosition()); + } + + private CharSequence getSubstring(MatchRange range) { + return getFullText().substring(range.getStart(), range.getEnd()); + } + + /** Extracts the matching string from the document. */ + private static String getPropertyValues( + GenericDocument document, String propertyName, int valueIndex) { + // In IcingLib snippeting is available for only 3 data types i.e String, double and + // long, so we need to check which of these three are requested. + // TODO (tytytyww): getPropertyStringArray takes property name, handle for property + // path. + // TODO (tytytyww): support double[] and long[]. + String[] values = document.getPropertyStringArray(propertyName); + if (values == null) { + throw new IllegalStateException("No content found for requested property path!"); + } + return values[valueIndex]; + } + } + + /** + * Class providing the position range of matching information. + * + * <p> All ranges are finite, and the left side of the range is always {@code <=} the right + * side of the range. + * + * <p> Example: MatchRange(0, 100) represent a hundred ints from 0 to 99." + * + */ + public static final class MatchRange { + private final int mEnd; + private final int mStart; + + /** + * Creates a new immutable range. + * <p> The endpoints are {@code [start, end)}; that is the range is bounded. {@code start} + * must be lesser or equal to {@code end}. + * + * @param start The start point (inclusive) + * @param end The end point (exclusive) + * @hide + */ + + public MatchRange(int start, int end) { + if (start > end) { + throw new IllegalArgumentException("Start point must be less than or equal to " + + "end point"); + } + mStart = start; + mEnd = end; + } + + /** Gets the start point (inclusive). */ + public int getStart() { + return mStart; + } + + /** Gets the end point (exclusive). */ + public int getEnd() { + return mEnd; + } + + @Override + public boolean equals(@Nullable Object other) { + if (this == other) { + return true; + } + if (!(other instanceof MatchRange)) { + return false; + } + MatchRange otherMatchRange = (MatchRange) other; + return this.getStart() == otherMatchRange.getStart() + && this.getEnd() == otherMatchRange.getEnd(); + } + + @Override + @NonNull + public String toString() { + return "MatchRange { start: " + mStart + " , end: " + mEnd + "}"; + } + + @Override + public int hashCode() { + return Objects.hash(mStart, mEnd); + } + } +} diff --git a/apex/appsearch/framework/java/android/app/appsearch/SearchResults.java b/apex/appsearch/framework/java/android/app/appsearch/SearchResults.java index 7287fe68f519..9f376250f1a6 100644 --- a/apex/appsearch/framework/java/android/app/appsearch/SearchResults.java +++ b/apex/appsearch/framework/java/android/app/appsearch/SearchResults.java @@ -17,112 +17,62 @@ package android.app.appsearch; import android.annotation.NonNull; -import android.annotation.Nullable; - -import com.google.android.icing.proto.SearchResultProto; -import com.google.android.icing.proto.SnippetMatchProto; -import com.google.android.icing.proto.SnippetProto; +import android.os.Bundle; +import android.os.Parcel; +import android.os.Parcelable; import java.util.ArrayList; -import java.util.Iterator; import java.util.List; -import java.util.NoSuchElementException; /** - * SearchResults are a list of results that are returned from a query. Each result from this - * list contains a document and may contain other fields like snippets based on request. - * This iterator class is not thread safe. + * Structure for transmitting a page of search results across binder. * @hide */ -public final class SearchResults implements Iterator<SearchResults.Result> { - - private final SearchResultProto mSearchResultProto; - private int mNextIdx; +public final class SearchResults implements Parcelable { + final List<SearchResult> mResults; + final long mNextPageToken; - /** @hide */ - public SearchResults(SearchResultProto searchResultProto) { - mSearchResultProto = searchResultProto; + public SearchResults(@NonNull List<SearchResult> results, long nextPageToken) { + mResults = results; + mNextPageToken = nextPageToken; } - @Override - public boolean hasNext() { - return mNextIdx < mSearchResultProto.getResultsCount(); + private SearchResults(@NonNull Parcel in) { + List<Bundle> resultBundles = in.readArrayList(/*loader=*/ null); + mResults = new ArrayList<>(resultBundles.size()); + for (int i = 0; i < resultBundles.size(); i++) { + SearchResult searchResult = new SearchResult(resultBundles.get(i)); + mResults.add(searchResult); + } + mNextPageToken = in.readLong(); } - @NonNull @Override - public Result next() { - if (!hasNext()) { - throw new NoSuchElementException(); + public void writeToParcel(@NonNull Parcel dest, int flags) { + List<Bundle> resultBundles = new ArrayList<>(mResults.size()); + for (int i = 0; i < mResults.size(); i++) { + resultBundles.add(mResults.get(i).getBundle()); } - Result result = new Result(mSearchResultProto.getResults(mNextIdx)); - mNextIdx++; - return result; + dest.writeList(resultBundles); + dest.writeLong(mNextPageToken); } + @Override + public int describeContents() { + return 0; + } - - /** - * This class represents the result obtained from the query. It will contain the document which - * which matched the specified query string and specifications. - * @hide - */ - public static final class Result { - private final SearchResultProto.ResultProto mResultProto; - - @Nullable - private AppSearchDocument mDocument; - - private Result(SearchResultProto.ResultProto resultProto) { - mResultProto = resultProto; - } - - /** - * Contains the matching {@link AppSearchDocument}. - * @return Document object which matched the query. - * @hide - */ + public static final Creator<SearchResults> CREATOR = new Creator<SearchResults>() { @NonNull - public AppSearchDocument getDocument() { - if (mDocument == null) { - mDocument = new AppSearchDocument(mResultProto.getDocument()); - } - return mDocument; + @Override + public SearchResults createFromParcel(@NonNull Parcel in) { + return new SearchResults(in); } - /** - * Contains a list of Snippets that matched the request. Only populated when requested in - * {@link SearchSpec.Builder#setMaxSnippetSize(int)}. - * @return List of matches based on {@link SearchSpec}, if snippeting is disabled and this - * method is called it will return {@code null}. Users can also restrict snippet population - * using {@link SearchSpec.Builder#setNumToSnippet} and - * {@link SearchSpec.Builder#setNumMatchesPerProperty}, for all results after that value - * this method will return {@code null}. - * @hide - */ - // TODO(sidchhabra): Replace Document with proper constructor. - @Nullable - public List<MatchInfo> getMatchInfo() { - if (!mResultProto.hasSnippet()) { - return null; - } - AppSearchDocument document = getDocument(); - List<MatchInfo> matchList = new ArrayList<>(); - for (Iterator entryProtoIterator = mResultProto.getSnippet() - .getEntriesList().iterator(); entryProtoIterator.hasNext(); ) { - SnippetProto.EntryProto entry = (SnippetProto.EntryProto) entryProtoIterator.next(); - for (Iterator snippetMatchProtoIterator = entry.getSnippetMatchesList().iterator(); - snippetMatchProtoIterator.hasNext(); ) { - matchList.add(new MatchInfo(entry.getPropertyName(), - (SnippetMatchProto) snippetMatchProtoIterator.next(), document)); - } - } - return matchList; + @NonNull + @Override + public SearchResults[] newArray(int size) { + return new SearchResults[size]; } - } - - @Override - public String toString() { - return mSearchResultProto.toString(); - } + }; } diff --git a/apex/appsearch/framework/java/android/app/appsearch/SearchSpec.java b/apex/appsearch/framework/java/android/app/appsearch/SearchSpec.java index 817c3ef5e028..c8719059fa8c 100644 --- a/apex/appsearch/framework/java/android/app/appsearch/SearchSpec.java +++ b/apex/appsearch/framework/java/android/app/appsearch/SearchSpec.java @@ -249,8 +249,10 @@ public final class SearchSpec { /** * Only the first {@code snippetCount} documents based on the ranking strategy * will have snippet information provided. - * <p>If set to 0 (default), snippeting is disabled and - * {@link SearchResults.Result#getMatches} will return {@code null} for that result. + * + * <p>If set to 0 (default), snippeting is disabled and {@link SearchResult#getMatches} will + * return {@code null} for that result. + * * <p>The value should be set in range[0, 10k]. */ @NonNull @@ -264,8 +266,10 @@ public final class SearchSpec { /** * Only the first {@code matchesCountPerProperty} matches for a every property of * {@link GenericDocument} will contain snippet information. - * <p>If set to 0, snippeting is disabled and {@link SearchResults.Result#getMatches} - * will return {@code null} for that result. + * + * <p>If set to 0, snippeting is disabled and {@link SearchResult#getMatches} will return + * {@code null} for that result. + * * <p>The value should be set in range[0, 10k]. */ @NonNull diff --git a/apex/appsearch/framework/java/android/app/appsearch/SetSchemaRequest.java b/apex/appsearch/framework/java/android/app/appsearch/SetSchemaRequest.java new file mode 100644 index 000000000000..b2e9d469764d --- /dev/null +++ b/apex/appsearch/framework/java/android/app/appsearch/SetSchemaRequest.java @@ -0,0 +1,101 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app.appsearch; + +import android.annotation.NonNull; +import android.util.ArraySet; + +import com.android.internal.util.Preconditions; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Set; + +/** + * Encapsulates a request to update the schema of an {@link AppSearchManager} database. + * + * @see AppSearchManager#setSchema + * @hide + */ +public final class SetSchemaRequest { + private final Set<AppSearchSchema> mSchemas; + private final boolean mForceOverride; + + SetSchemaRequest(Set<AppSearchSchema> schemas, boolean forceOverride) { + mSchemas = schemas; + mForceOverride = forceOverride; + } + + /** @hide */ + + @NonNull + public Set<AppSearchSchema> getSchemas() { + return mSchemas; + } + + /** @hide */ + + public boolean isForceOverride() { + return mForceOverride; + } + + /** Builder for {@link SetSchemaRequest} objects. */ + public static final class Builder { + private final Set<AppSearchSchema> mSchemas = new ArraySet<>(); + private boolean mForceOverride = false; + private boolean mBuilt = false; + + /** Adds one or more types to the schema. */ + @NonNull + public Builder addSchema(@NonNull AppSearchSchema... schemas) { + Preconditions.checkNotNull(schemas); + return addSchema(Arrays.asList(schemas)); + } + + /** Adds one or more types to the schema. */ + @NonNull + public Builder addSchema(@NonNull Collection<AppSearchSchema> schemas) { + Preconditions.checkState(!mBuilt, "Builder has already been used"); + Preconditions.checkNotNull(schemas); + mSchemas.addAll(schemas); + return this; + } + + /** + * Configures the {@link SetSchemaRequest} to delete any existing documents that don't + * follow the new schema. + * + * <p>By default, this is {@code false} and schema incompatibility causes the + * {@link AppSearchManager#setSchema} call to fail. + * + * @see AppSearchManager#setSchema + */ + @NonNull + public Builder setForceOverride(boolean forceOverride) { + mForceOverride = forceOverride; + return this; + } + + /** Builds a new {@link SetSchemaRequest}. */ + @NonNull + public SetSchemaRequest build() { + Preconditions.checkState(!mBuilt, "Builder has already been used"); + mBuilt = true; + return new SetSchemaRequest(mSchemas, mForceOverride); + } + } +} diff --git a/apex/appsearch/framework/java/android/app/appsearch/exceptions/AppSearchException.java b/apex/appsearch/framework/java/android/app/appsearch/exceptions/AppSearchException.java index 00f6e75afe54..d490469be3d6 100644 --- a/apex/appsearch/framework/java/android/app/appsearch/exceptions/AppSearchException.java +++ b/apex/appsearch/framework/java/android/app/appsearch/exceptions/AppSearchException.java @@ -54,6 +54,10 @@ public class AppSearchException extends Exception { mResultCode = resultCode; } + public @AppSearchResult.ResultCode int getResultCode() { + return mResultCode; + } + /** * Converts this {@link java.lang.Exception} into a failed {@link AppSearchResult} */ diff --git a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java index 4bc0c39bb6c9..7cd6ee24cb20 100644 --- a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java +++ b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java @@ -17,10 +17,12 @@ package com.android.server.appsearch; import android.annotation.NonNull; import android.app.appsearch.AppSearchBatchResult; -import android.app.appsearch.AppSearchDocument; import android.app.appsearch.AppSearchResult; import android.app.appsearch.AppSearchSchema; +import android.app.appsearch.GenericDocument; import android.app.appsearch.IAppSearchManager; +import android.app.appsearch.SearchResult; +import android.app.appsearch.SearchResults; import android.app.appsearch.SearchSpec; import android.app.appsearch.exceptions.AppSearchException; import android.content.Context; @@ -32,7 +34,9 @@ import com.android.internal.infra.AndroidFuture; import com.android.internal.util.Preconditions; import com.android.server.SystemService; import com.android.server.appsearch.external.localbackend.AppSearchImpl; +import com.android.server.appsearch.external.localbackend.converter.GenericDocumentToProtoConverter; import com.android.server.appsearch.external.localbackend.converter.SchemaToProtoConverter; +import com.android.server.appsearch.external.localbackend.converter.SearchResultToProtoConverter; import com.android.server.appsearch.external.localbackend.converter.SearchSpecToProtoConverter; import com.google.android.icing.proto.DocumentProto; @@ -49,6 +53,7 @@ import java.util.List; */ public class AppSearchManagerService extends SystemService { private static final String TAG = "AppSearchManagerService"; + private static final char CALLING_NAME_DATABASE_DELIMITER = '$'; public AppSearchManagerService(Context context) { super(context); @@ -62,6 +67,7 @@ public class AppSearchManagerService extends SystemService { private class Stub extends IAppSearchManager.Stub { @Override public void setSchema( + @NonNull String databaseName, @NonNull List<Bundle> schemaBundles, boolean forceOverride, @NonNull AndroidFuture<AppSearchResult> callback) { @@ -78,9 +84,9 @@ public class AppSearchManagerService extends SystemService { schemaProtoBuilder.addTypes(schemaTypeProto); } AppSearchImpl impl = ImplInstanceManager.getInstance(getContext(), callingUserId); - String databaseName = makeDatabaseName(callingUid); + databaseName = rewriteDatabaseNameWithUid(databaseName, callingUid); impl.setSchema(databaseName, schemaProtoBuilder.build(), forceOverride); - callback.complete(AppSearchResult.newSuccessfulResult(/*value=*/ null)); + callback.complete(AppSearchResult.newSuccessfulResult(/*result=*/ null)); } catch (Throwable t) { callback.complete(throwableToFailedResult(t)); } finally { @@ -90,24 +96,25 @@ public class AppSearchManagerService extends SystemService { @Override public void putDocuments( - @NonNull List documentsBytes, + @NonNull String databaseName, + @NonNull List<Bundle> documentBundles, @NonNull AndroidFuture<AppSearchBatchResult> callback) { - Preconditions.checkNotNull(documentsBytes); + Preconditions.checkNotNull(documentBundles); Preconditions.checkNotNull(callback); int callingUid = Binder.getCallingUidOrThrow(); int callingUserId = UserHandle.getUserId(callingUid); final long callingIdentity = Binder.clearCallingIdentity(); try { AppSearchImpl impl = ImplInstanceManager.getInstance(getContext(), callingUserId); - String databaseName = makeDatabaseName(callingUid); + databaseName = rewriteDatabaseNameWithUid(databaseName, callingUid); AppSearchBatchResult.Builder<String, Void> resultBuilder = new AppSearchBatchResult.Builder<>(); - for (int i = 0; i < documentsBytes.size(); i++) { - byte[] documentBytes = (byte[]) documentsBytes.get(i); - DocumentProto document = DocumentProto.parseFrom(documentBytes); + for (int i = 0; i < documentBundles.size(); i++) { + GenericDocument document = new GenericDocument(documentBundles.get(i)); + DocumentProto documentProto = GenericDocumentToProtoConverter.convert(document); try { - impl.putDocument(databaseName, document); - resultBuilder.setSuccess(document.getUri(), /*value=*/ null); + impl.putDocument(databaseName, documentProto); + resultBuilder.setSuccess(document.getUri(), /*result=*/ null); } catch (Throwable t) { resultBuilder.setResult(document.getUri(), throwableToFailedResult(t)); } @@ -121,7 +128,7 @@ public class AppSearchManagerService extends SystemService { } @Override - public void getDocuments( + public void getDocuments(@NonNull String databaseName, @NonNull String namespace, @NonNull List<String> uris, @NonNull AndroidFuture<AppSearchBatchResult> callback) { Preconditions.checkNotNull(uris); Preconditions.checkNotNull(callback); @@ -130,19 +137,21 @@ public class AppSearchManagerService extends SystemService { final long callingIdentity = Binder.clearCallingIdentity(); try { AppSearchImpl impl = ImplInstanceManager.getInstance(getContext(), callingUserId); - String databaseName = makeDatabaseName(callingUid); - AppSearchBatchResult.Builder<String, byte[]> resultBuilder = + databaseName = rewriteDatabaseNameWithUid(databaseName, callingUid); + AppSearchBatchResult.Builder<String, Bundle> resultBuilder = new AppSearchBatchResult.Builder<>(); for (int i = 0; i < uris.size(); i++) { String uri = uris.get(i); try { - DocumentProto document = impl.getDocument( - databaseName, AppSearchDocument.DEFAULT_NAMESPACE, uri); - if (document == null) { + DocumentProto documentProto = impl.getDocument( + databaseName, namespace, uri); + if (documentProto == null) { resultBuilder.setFailure( uri, AppSearchResult.RESULT_NOT_FOUND, /*errorMessage=*/ null); } else { - resultBuilder.setSuccess(uri, document.toByteArray()); + GenericDocument genericDocument = + GenericDocumentToProtoConverter.convert(documentProto); + resultBuilder.setSuccess(uri, genericDocument.getBundle()); } } catch (Throwable t) { resultBuilder.setResult(uri, throwableToFailedResult(t)); @@ -159,6 +168,7 @@ public class AppSearchManagerService extends SystemService { // TODO(sidchhabra): Do this in a threadpool. @Override public void query( + @NonNull String databaseName, @NonNull String queryExpression, @NonNull Bundle searchSpecBundle, @NonNull AndroidFuture<AppSearchResult> callback) { @@ -175,15 +185,18 @@ public class AppSearchManagerService extends SystemService { searchSpecProto = searchSpecProto.toBuilder() .setQuery(queryExpression).build(); AppSearchImpl impl = ImplInstanceManager.getInstance(getContext(), callingUserId); - String databaseName = makeDatabaseName(callingUid); + databaseName = rewriteDatabaseNameWithUid(databaseName, callingUid); // TODO(adorokhine): handle pagination SearchResultProto searchResultProto = impl.query( databaseName, searchSpecProto, SearchSpecToProtoConverter.toResultSpecProto(searchSpec), SearchSpecToProtoConverter.toScoringSpecProto(searchSpec)); - callback.complete( - AppSearchResult.newSuccessfulResult(searchResultProto.toByteArray())); + List<SearchResult> searchResultList = + SearchResultToProtoConverter.convert(searchResultProto); + SearchResults searchResults = + new SearchResults(searchResultList, searchResultProto.getNextPageToken()); + callback.complete(AppSearchResult.newSuccessfulResult(searchResults)); } catch (Throwable t) { callback.complete(throwableToFailedResult(t)); } finally { @@ -192,7 +205,8 @@ public class AppSearchManagerService extends SystemService { } @Override - public void delete(List<String> uris, AndroidFuture<AppSearchBatchResult> callback) { + public void delete(@NonNull String databaseName, @NonNull String namespace, + List<String> uris, AndroidFuture<AppSearchBatchResult> callback) { Preconditions.checkNotNull(uris); Preconditions.checkNotNull(callback); int callingUid = Binder.getCallingUidOrThrow(); @@ -200,14 +214,14 @@ public class AppSearchManagerService extends SystemService { final long callingIdentity = Binder.clearCallingIdentity(); try { AppSearchImpl impl = ImplInstanceManager.getInstance(getContext(), callingUserId); - String databaseName = makeDatabaseName(callingUid); + databaseName = rewriteDatabaseNameWithUid(databaseName, callingUid); AppSearchBatchResult.Builder<String, Void> resultBuilder = new AppSearchBatchResult.Builder<>(); for (int i = 0; i < uris.size(); i++) { String uri = uris.get(i); try { - impl.remove(databaseName, AppSearchDocument.DEFAULT_NAMESPACE, uri); - resultBuilder.setSuccess(uri, /*value= */null); + impl.remove(databaseName, namespace, uri); + resultBuilder.setSuccess(uri, /*result= */null); } catch (Throwable t) { resultBuilder.setResult(uri, throwableToFailedResult(t)); } @@ -221,7 +235,7 @@ public class AppSearchManagerService extends SystemService { } @Override - public void deleteByTypes( + public void deleteByTypes(@NonNull String databaseName, List<String> schemaTypes, AndroidFuture<AppSearchBatchResult> callback) { Preconditions.checkNotNull(schemaTypes); Preconditions.checkNotNull(callback); @@ -230,14 +244,14 @@ public class AppSearchManagerService extends SystemService { final long callingIdentity = Binder.clearCallingIdentity(); try { AppSearchImpl impl = ImplInstanceManager.getInstance(getContext(), callingUserId); - String databaseName = makeDatabaseName(callingUid); + databaseName = rewriteDatabaseNameWithUid(databaseName, callingUid); AppSearchBatchResult.Builder<String, Void> resultBuilder = new AppSearchBatchResult.Builder<>(); for (int i = 0; i < schemaTypes.size(); i++) { String schemaType = schemaTypes.get(i); try { impl.removeByType(databaseName, schemaType); - resultBuilder.setSuccess(schemaType, /*value=*/ null); + resultBuilder.setSuccess(schemaType, /*result=*/ null); } catch (Throwable t) { resultBuilder.setResult(schemaType, throwableToFailedResult(t)); } @@ -251,14 +265,15 @@ public class AppSearchManagerService extends SystemService { } @Override - public void deleteAll(@NonNull AndroidFuture<AppSearchResult> callback) { + public void deleteAll(@NonNull String databaseName, + @NonNull AndroidFuture<AppSearchResult> callback) { Preconditions.checkNotNull(callback); int callingUid = Binder.getCallingUidOrThrow(); int callingUserId = UserHandle.getUserId(callingUid); final long callingIdentity = Binder.clearCallingIdentity(); try { AppSearchImpl impl = ImplInstanceManager.getInstance(getContext(), callingUserId); - String databaseName = makeDatabaseName(callingUid); + databaseName = rewriteDatabaseNameWithUid(databaseName, callingUid); impl.removeAll(databaseName); callback.complete(AppSearchResult.newSuccessfulResult(null)); } catch (Throwable t) { @@ -269,13 +284,13 @@ public class AppSearchManagerService extends SystemService { } /** - * Returns a unique database name for the given uid. + * Rewrites the database name by adding a prefix of unique name for the given uid. * * <p>The current implementation returns the package name of the app with this uid in a * format like {@code com.example.package} or {@code com.example.sharedname:5678}. */ @NonNull - private String makeDatabaseName(int callingUid) { + private String rewriteDatabaseNameWithUid(String databaseName, int callingUid) { // For regular apps, this call will return the package name. If callingUid is an // android:sharedUserId, this value may be another type of name and have a :uid suffix. String callingUidName = getContext().getPackageManager().getNameForUid(callingUid); @@ -284,7 +299,7 @@ public class AppSearchManagerService extends SystemService { throw new IllegalStateException( "Failed to look up package name for uid " + callingUid); } - return callingUidName; + return callingUidName + CALLING_NAME_DATABASE_DELIMITER + databaseName; } private <ValueType> AppSearchResult<ValueType> throwableToFailedResult( diff --git a/apex/appsearch/service/java/com/android/server/appsearch/ImplInstanceManager.java b/apex/appsearch/service/java/com/android/server/appsearch/ImplInstanceManager.java index c1e6b0fd205f..60f7005a7c0a 100644 --- a/apex/appsearch/service/java/com/android/server/appsearch/ImplInstanceManager.java +++ b/apex/appsearch/service/java/com/android/server/appsearch/ImplInstanceManager.java @@ -69,9 +69,7 @@ public final class ImplInstanceManager { private static AppSearchImpl createImpl(@NonNull Context context, @UserIdInt int userId) throws AppSearchException { File appSearchDir = getAppSearchDir(context, userId); - AppSearchImpl appSearchImpl = new AppSearchImpl(appSearchDir); - appSearchImpl.initialize(); - return appSearchImpl; + return AppSearchImpl.create(appSearchDir); } private static File getAppSearchDir(@NonNull Context context, @UserIdInt int userId) { diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localbackend/AppSearchImpl.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localbackend/AppSearchImpl.java index 462f45869b34..642378d992ac 100644 --- a/apex/appsearch/service/java/com/android/server/appsearch/external/localbackend/AppSearchImpl.java +++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localbackend/AppSearchImpl.java @@ -18,7 +18,6 @@ package com.android.server.appsearch.external.localbackend; import android.util.Log; -import android.annotation.AnyThread; import com.android.internal.annotations.GuardedBy; import android.annotation.NonNull; import android.annotation.Nullable; @@ -27,6 +26,7 @@ import com.android.internal.annotations.VisibleForTesting; import android.annotation.WorkerThread; import android.app.appsearch.AppSearchResult; import android.app.appsearch.exceptions.AppSearchException; +import com.android.internal.util.Preconditions; import com.google.android.icing.IcingSearchEngine; import com.google.android.icing.proto.DeleteByNamespaceResultProto; @@ -58,7 +58,6 @@ import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; -import java.util.concurrent.CountDownLatch; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; @@ -66,8 +65,7 @@ import java.util.concurrent.locks.ReentrantReadWriteLock; * Manages interaction with the native IcingSearchEngine and other components to implement AppSearch * functionality. * - * <p>Callers should call {@link #initialize} before using the AppSearchImpl instance. Never create - * two instances using the same folder. + * <p>Never create two instances using the same folder. * * <p>A single instance of {@link AppSearchImpl} can support all databases. Schemas and documents * are physically saved together in {@link IcingSearchEngine}, but logically isolated: @@ -106,9 +104,7 @@ public final class AppSearchImpl { static final int CHECK_OPTIMIZE_INTERVAL = 100; private final ReadWriteLock mReadWriteLock = new ReentrantReadWriteLock(); - private final CountDownLatch mInitCompleteLatch = new CountDownLatch(1); - private final File mIcingDir; - private IcingSearchEngine mIcingSearchEngine; + private final IcingSearchEngine mIcingSearchEngine; // The map contains schemaTypes and namespaces for all database. All values in the map have // been already added database name prefix. @@ -121,33 +117,24 @@ public final class AppSearchImpl { */ private int mOptimizeIntervalCount = 0; - /** Creates an instance of {@link AppSearchImpl} which writes data to the given folder. */ - @AnyThread - public AppSearchImpl(@NonNull File icingDir) { - mIcingDir = icingDir; - } - /** - * Initializes the underlying IcingSearchEngine. - * - * <p>This method belongs to mutate group. - * - * @throws AppSearchException on IcingSearchEngine error. + * Creates and initializes an instance of {@link AppSearchImpl} which writes data to the given + * folder. */ - public void initialize() throws AppSearchException { - if (isInitialized()) { - return; - } + @NonNull + public static AppSearchImpl create(@NonNull File icingDir) throws AppSearchException { + Preconditions.checkNotNull(icingDir); + return new AppSearchImpl(icingDir); + } + + private AppSearchImpl(@NonNull File icingDir) throws AppSearchException { boolean isReset = false; mReadWriteLock.writeLock().lock(); try { - // We synchronize here because we don't want to call IcingSearchEngine.initialize() more - // than once. It's unnecessary and can be a costly operation. - if (isInitialized()) { - return; - } + // We synchronize here because we don't want to call IcingSearchEngine.initialize() more + // than once. It's unnecessary and can be a costly operation. IcingSearchEngineOptions options = IcingSearchEngineOptions.newBuilder() - .setBaseDir(mIcingDir.getAbsolutePath()).build(); + .setBaseDir(icingDir.getAbsolutePath()).build(); mIcingSearchEngine = new IcingSearchEngine(options); InitializeResultProto initializeResultProto = mIcingSearchEngine.initialize(); @@ -170,7 +157,8 @@ public final class AppSearchImpl { for (String qualifiedNamespace : getAllNamespacesResultProto.getNamespacesList()) { addToMap(mNamespaceMap, getDatabaseName(qualifiedNamespace), qualifiedNamespace); } - mInitCompleteLatch.countDown(); + // TODO(b/155939114): It's possible to optimize after init, which would reduce the time + // to when we're able to serve queries. Consider moving this optimize call out. if (!isReset) { checkForOptimize(/* force= */ true); } @@ -179,12 +167,6 @@ public final class AppSearchImpl { } } - /** Checks if the internal state of {@link AppSearchImpl} has been initialized. */ - @AnyThread - public boolean isInitialized() { - return mInitCompleteLatch.getCount() == 0; - } - /** * Updates the AppSearch schema for this app. * @@ -195,12 +177,9 @@ public final class AppSearchImpl { * @param forceOverride Whether to force-apply the schema even if it is incompatible. Documents * which do not comply with the new schema will be deleted. * @throws AppSearchException on IcingSearchEngine error. - * @throws InterruptedException if the current thread was interrupted during execution. */ public void setSchema(@NonNull String databaseName, @NonNull SchemaProto origSchema, - boolean forceOverride) throws AppSearchException, InterruptedException { - awaitInitialized(); - + boolean forceOverride) throws AppSearchException { SchemaProto schemaProto = getSchemaProto(); SchemaProto.Builder existingSchemaBuilder = schemaProto.toBuilder(); @@ -212,10 +191,32 @@ public final class AppSearchImpl { SetSchemaResultProto setSchemaResultProto; mReadWriteLock.writeLock().lock(); try { - setSchemaResultProto = mIcingSearchEngine.setSchema(existingSchemaBuilder.build(), - forceOverride); - checkSuccess(setSchemaResultProto.getStatus()); + // Apply schema + setSchemaResultProto = + mIcingSearchEngine.setSchema(existingSchemaBuilder.build(), forceOverride); + + // Determine whether it succeeded. + try { + checkSuccess(setSchemaResultProto.getStatus()); + } catch (AppSearchException e) { + // Improve the error message by merging in information about incompatible types. + if (setSchemaResultProto.getDeletedSchemaTypesCount() > 0 + || setSchemaResultProto.getIncompatibleSchemaTypesCount() > 0) { + String newMessage = e.getMessage() + + "\n Deleted types: " + + setSchemaResultProto.getDeletedSchemaTypesList() + + "\n Incompatible types: " + + setSchemaResultProto.getIncompatibleSchemaTypesList(); + throw new AppSearchException(e.getResultCode(), newMessage, e.getCause()); + } else { + throw e; + } + } + + // Update derived data structures. mSchemaMap.put(databaseName, newTypeNames); + + // Determine whether to schedule an immediate optimize. if (setSchemaResultProto.getDeletedSchemaTypesCount() > 0 || (setSchemaResultProto.getIncompatibleSchemaTypesCount() > 0 && forceOverride)) { @@ -237,12 +238,9 @@ public final class AppSearchImpl { * @param databaseName The databaseName this document resides in. * @param document The document to index. * @throws AppSearchException on IcingSearchEngine error. - * @throws InterruptedException if the current thread was interrupted during execution. */ public void putDocument(@NonNull String databaseName, @NonNull DocumentProto document) - throws AppSearchException, InterruptedException { - awaitInitialized(); - + throws AppSearchException { DocumentProto.Builder documentBuilder = document.toBuilder(); rewriteDocumentTypes(getDatabasePrefix(databaseName), documentBuilder, /*add=*/ true); @@ -270,12 +268,10 @@ public final class AppSearchImpl { * @param uri The URI of the document to get. * @return The Document contents, or {@code null} if no such URI exists in the system. * @throws AppSearchException on IcingSearchEngine error. - * @throws InterruptedException if the current thread was interrupted during execution. */ @Nullable public DocumentProto getDocument(@NonNull String databaseName, @NonNull String namespace, - @NonNull String uri) throws AppSearchException, InterruptedException { - awaitInitialized(); + @NonNull String uri) throws AppSearchException { GetResultProto getResultProto; mReadWriteLock.readLock().lock(); try { @@ -303,16 +299,13 @@ public final class AppSearchImpl { * @return The results of performing this search The proto might have no {@code results} if no * documents matched the query. * @throws AppSearchException on IcingSearchEngine error. - * @throws InterruptedException if the current thread was interrupted during execution. */ @NonNull public SearchResultProto query( @NonNull String databaseName, @NonNull SearchSpecProto searchSpec, @NonNull ResultSpecProto resultSpec, - @NonNull ScoringSpecProto scoringSpec) throws AppSearchException, InterruptedException { - awaitInitialized(); - + @NonNull ScoringSpecProto scoringSpec) throws AppSearchException { SearchSpecProto.Builder searchSpecBuilder = searchSpec.toBuilder(); SearchResultProto searchResultProto; mReadWriteLock.readLock().lock(); @@ -347,13 +340,10 @@ public final class AppSearchImpl { * @param nextPageToken The token of pre-loaded results of previously executed query. * @return The next page of results of previously executed query. * @throws AppSearchException on IcingSearchEngine error. - * @throws InterruptedException if the current thread was interrupted during execution. */ @NonNull public SearchResultProto getNextPage(@NonNull String databaseName, long nextPageToken) - throws AppSearchException, InterruptedException { - awaitInitialized(); - + throws AppSearchException { SearchResultProto searchResultProto = mIcingSearchEngine.getNextPage(nextPageToken); checkSuccess(searchResultProto.getStatus()); if (searchResultProto.getResultsCount() == 0) { @@ -367,8 +357,7 @@ public final class AppSearchImpl { * @param nextPageToken The token of pre-loaded results of previously executed query to be * Invalidated. */ - public void invalidateNextPageToken(long nextPageToken) throws InterruptedException { - awaitInitialized(); + public void invalidateNextPageToken(long nextPageToken) { mIcingSearchEngine.invalidateNextPageToken(nextPageToken); } @@ -381,12 +370,9 @@ public final class AppSearchImpl { * @param namespace Namespace of the document to remove. * @param uri URI of the document to remove. * @throws AppSearchException on IcingSearchEngine error. - * @throws InterruptedException if the current thread was interrupted during execution. */ public void remove(@NonNull String databaseName, @NonNull String namespace, - @NonNull String uri) throws AppSearchException, InterruptedException { - awaitInitialized(); - + @NonNull String uri) throws AppSearchException { String qualifiedNamespace = getDatabasePrefix(databaseName) + namespace; DeleteResultProto deleteResultProto; mReadWriteLock.writeLock().lock(); @@ -407,12 +393,9 @@ public final class AppSearchImpl { * @param databaseName The databaseName that contains documents of schemaType. * @param schemaType The schemaType of documents to remove. * @throws AppSearchException on IcingSearchEngine error. - * @throws InterruptedException if the current thread was interrupted during execution. */ public void removeByType(@NonNull String databaseName, @NonNull String schemaType) - throws AppSearchException, InterruptedException { - awaitInitialized(); - + throws AppSearchException { String qualifiedType = getDatabasePrefix(databaseName) + schemaType; DeleteBySchemaTypeResultProto deleteBySchemaTypeResultProto; mReadWriteLock.writeLock().lock(); @@ -437,12 +420,9 @@ public final class AppSearchImpl { * @param databaseName The databaseName that contains documents of namespace. * @param namespace The namespace of documents to remove. * @throws AppSearchException on IcingSearchEngine error. - * @throws InterruptedException if the current thread was interrupted during execution. */ public void removeByNamespace(@NonNull String databaseName, @NonNull String namespace) - throws AppSearchException, InterruptedException { - awaitInitialized(); - + throws AppSearchException { String qualifiedNamespace = getDatabasePrefix(databaseName) + namespace; DeleteByNamespaceResultProto deleteByNamespaceResultProto; mReadWriteLock.writeLock().lock(); @@ -469,11 +449,9 @@ public final class AppSearchImpl { * * @param databaseName The databaseName to remove all documents from. * @throws AppSearchException on IcingSearchEngine error. - * @throws InterruptedException if the current thread was interrupted during execution. */ public void removeAll(@NonNull String databaseName) - throws AppSearchException, InterruptedException { - awaitInitialized(); + throws AppSearchException { mReadWriteLock.writeLock().lock(); try { Set<String> existingNamespaces = mNamespaceMap.get(databaseName); @@ -733,15 +711,6 @@ public final class AppSearchImpl { } /** - * Waits for the instance to become initialized. - * - * @throws InterruptedException if the current thread was interrupted during waiting. - */ - private void awaitInitialized() throws InterruptedException { - mInitCompleteLatch.await(); - } - - /** * Checks the given status code and throws an {@link AppSearchException} if code is an error. * * @throws AppSearchException on error codes. diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localbackend/converter/GenericDocumentToProtoConverter.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localbackend/converter/GenericDocumentToProtoConverter.java new file mode 100644 index 000000000000..fdeb90dc9b0e --- /dev/null +++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localbackend/converter/GenericDocumentToProtoConverter.java @@ -0,0 +1,152 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.appsearch.external.localbackend.converter; + +import android.os.Bundle; + +import android.annotation.NonNull; + +import android.app.appsearch.GenericDocument; +import com.android.internal.util.Preconditions; + +import com.google.android.icing.proto.DocumentProto; +import com.google.android.icing.proto.PropertyProto; +import com.google.protobuf.ByteString; + +import java.util.ArrayList; +import java.util.Collections; + +/** + * Translates a {@link GenericDocument} into a {@link DocumentProto}. + * @hide + */ + +public final class GenericDocumentToProtoConverter { + private GenericDocumentToProtoConverter() {} + + /** Converts a {@link GenericDocument} into a {@link DocumentProto}. */ + @NonNull + @SuppressWarnings("unchecked") + public static DocumentProto convert(@NonNull GenericDocument document) { + Preconditions.checkNotNull(document); + Bundle properties = document.getBundle().getBundle(GenericDocument.PROPERTIES_FIELD); + DocumentProto.Builder mProtoBuilder = DocumentProto.newBuilder(); + mProtoBuilder.setUri(document.getUri()) + .setSchema(document.getSchemaType()) + .setNamespace(document.getNamespace()) + .setScore(document.getScore()) + .setTtlMs(document.getTtlMillis()) + .setCreationTimestampMs(document.getCreationTimestampMillis()); + ArrayList<String> keys = new ArrayList<>(properties.keySet()); + Collections.sort(keys); + for (int i = 0; i < keys.size(); i++) { + String name = keys.get(i); + Object values = properties.get(name); + PropertyProto.Builder propertyProto = PropertyProto.newBuilder().setName(name); + if (values instanceof boolean[]) { + for (boolean value : (boolean[]) values) { + propertyProto.addBooleanValues(value); + } + } else if (values instanceof long[]) { + for (long value : (long[]) values) { + propertyProto.addInt64Values(value); + } + } else if (values instanceof double[]) { + for (double value : (double[]) values) { + propertyProto.addDoubleValues(value); + } + } else if (values instanceof String[]) { + for (String value : (String[]) values) { + propertyProto.addStringValues(value); + } + } else if (values instanceof ArrayList) { + for (Bundle bundle : (ArrayList<Bundle>) values) { + byte[] value = bundle.getByteArray(GenericDocument.BYTE_ARRAY_FIELD); + propertyProto.addBytesValues(ByteString.copyFrom(value)); + } + } else if (values instanceof Bundle[]) { + for (Bundle bundle : (Bundle[]) values) { + GenericDocument value = new GenericDocument(bundle); + propertyProto.addDocumentValues(convert(value)); + } + } else { + throw new IllegalStateException( + "Property \"" + name + "\" has unsupported value type \"" + + values.getClass().getSimpleName() + "\""); + } + mProtoBuilder.addProperties(propertyProto); + } + return mProtoBuilder.build(); + } + + /** Converts a {@link DocumentProto} into a {@link GenericDocument}. */ + @NonNull + public static GenericDocument convert(@NonNull DocumentProto proto) { + Preconditions.checkNotNull(proto); + GenericDocument.Builder<?> documentBuilder = + new GenericDocument.Builder<>(proto.getUri(), proto.getSchema()) + .setNamespace(proto.getNamespace()) + .setScore(proto.getScore()) + .setTtlMillis(proto.getTtlMs()) + .setCreationTimestampMillis(proto.getCreationTimestampMs()); + + for (int i = 0; i < proto.getPropertiesCount(); i++) { + PropertyProto property = proto.getProperties(i); + String name = property.getName(); + if (property.getBooleanValuesCount() > 0) { + boolean[] values = new boolean[property.getBooleanValuesCount()]; + for (int j = 0; j < values.length; j++) { + values[j] = property.getBooleanValues(j); + } + documentBuilder.setProperty(name, values); + } else if (property.getInt64ValuesCount() > 0) { + long[] values = new long[property.getInt64ValuesCount()]; + for (int j = 0; j < values.length; j++) { + values[j] = property.getInt64Values(j); + } + documentBuilder.setProperty(name, values); + } else if (property.getDoubleValuesCount() > 0) { + double[] values = new double[property.getDoubleValuesCount()]; + for (int j = 0; j < values.length; j++) { + values[j] = property.getDoubleValues(j); + } + documentBuilder.setProperty(name, values); + } else if (property.getStringValuesCount() > 0) { + String[] values = new String[property.getStringValuesCount()]; + for (int j = 0; j < values.length; j++) { + values[j] = property.getStringValues(j); + } + documentBuilder.setProperty(name, values); + } else if (property.getBytesValuesCount() > 0) { + byte[][] values = new byte[property.getBytesValuesCount()][]; + for (int j = 0; j < values.length; j++) { + values[j] = property.getBytesValues(j).toByteArray(); + } + documentBuilder.setProperty(name, values); + } else if (property.getDocumentValuesCount() > 0) { + GenericDocument[] values = new GenericDocument[property.getDocumentValuesCount()]; + for (int j = 0; j < values.length; j++) { + values[j] = convert(property.getDocumentValues(j)); + } + documentBuilder.setProperty(name, values); + } else { + throw new IllegalStateException("Unknown type of value: " + name); + } + } + return documentBuilder.build(); + } +} diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localbackend/converter/SearchResultToProtoConverter.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localbackend/converter/SearchResultToProtoConverter.java new file mode 100644 index 000000000000..524c80dd0609 --- /dev/null +++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localbackend/converter/SearchResultToProtoConverter.java @@ -0,0 +1,96 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.appsearch.external.localbackend.converter; + +import android.os.Bundle; + +import android.annotation.NonNull; + +import android.app.appsearch.GenericDocument; +import android.app.appsearch.SearchResult; +import android.app.appsearch.SearchResults; + +import com.google.android.icing.proto.SearchResultProto; +import com.google.android.icing.proto.SnippetMatchProto; +import com.google.android.icing.proto.SnippetProto; + +import java.util.ArrayList; +import java.util.List; + +/** + * Translates a {@link SearchResultProto} into {@link SearchResults}. + * @hide + */ + +public class SearchResultToProtoConverter { + private SearchResultToProtoConverter() {} + + /** Translates a {@link SearchResultProto} into a list of {@link SearchResult}. */ + @NonNull + public static List<SearchResult> convert(@NonNull SearchResultProto searchResultProto) { + List<SearchResult> results = new ArrayList<>(searchResultProto.getResultsCount()); + for (int i = 0; i < searchResultProto.getResultsCount(); i++) { + results.add(convertSearchResult(searchResultProto.getResults(i))); + } + return results; + } + + /** Translate a {@link SearchResultProto.ResultProto} into {@link SearchResult}. */ + @NonNull + static SearchResult convertSearchResult(@NonNull SearchResultProto.ResultProto proto) { + Bundle bundle = new Bundle(); + GenericDocument document = GenericDocumentToProtoConverter.convert(proto.getDocument()); + bundle.putBundle(SearchResult.DOCUMENT_FIELD, document.getBundle()); + + ArrayList<Bundle> matchList = null; + if (proto.hasSnippet()) { + matchList = new ArrayList<>(); + for (int i = 0; i < proto.getSnippet().getEntriesCount(); i++) { + SnippetProto.EntryProto entry = proto.getSnippet().getEntries(i); + for (int j = 0; j < entry.getSnippetMatchesCount(); j++) { + Bundle matchInfoBundle = convertToMatchInfoBundle( + entry.getSnippetMatches(j), entry.getPropertyName()); + matchList.add(matchInfoBundle); + } + } + } + bundle.putParcelableArrayList(SearchResult.MATCHES_FIELD, matchList); + + return new SearchResult(bundle); + } + + private static Bundle convertToMatchInfoBundle( + SnippetMatchProto snippetMatchProto, String propertyPath) { + Bundle bundle = new Bundle(); + bundle.putString(SearchResult.MatchInfo.PROPERTY_PATH_FIELD, propertyPath); + bundle.putInt( + SearchResult.MatchInfo.VALUES_INDEX_FIELD, snippetMatchProto.getValuesIndex()); + bundle.putInt( + SearchResult.MatchInfo.EXACT_MATCH_POSITION_LOWER_FIELD, + snippetMatchProto.getExactMatchPosition()); + bundle.putInt( + SearchResult.MatchInfo.EXACT_MATCH_POSITION_UPPER_FIELD, + snippetMatchProto.getExactMatchPosition() + snippetMatchProto.getExactMatchBytes()); + bundle.putInt( + SearchResult.MatchInfo.WINDOW_POSITION_LOWER_FIELD, + snippetMatchProto.getWindowPosition()); + bundle.putInt( + SearchResult.MatchInfo.WINDOW_POSITION_UPPER_FIELD, + snippetMatchProto.getWindowPosition() + snippetMatchProto.getWindowBytes()); + return bundle; + } +} diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobStoreUtils.java b/apex/blobstore/service/java/com/android/server/blob/BlobStoreUtils.java index 1d07e88773c3..8f9427303dff 100644 --- a/apex/blobstore/service/java/com/android/server/blob/BlobStoreUtils.java +++ b/apex/blobstore/service/java/com/android/server/blob/BlobStoreUtils.java @@ -24,6 +24,7 @@ import android.annotation.Nullable; import android.content.Context; import android.content.pm.PackageManager; import android.content.res.Resources; +import android.os.UserHandle; import android.text.format.TimeMigrationUtils; import android.util.Slog; @@ -34,8 +35,8 @@ class BlobStoreUtils { static Resources getPackageResources(@NonNull Context context, @NonNull String packageName, int userId) { try { - return context.getPackageManager() - .getResourcesForApplicationAsUser(packageName, userId); + return context.createContextAsUser(UserHandle.of(userId), /* flags */ 0) + .getPackageManager().getResourcesForApplication(packageName); } catch (PackageManager.NameNotFoundException e) { Slog.d(TAG, "Unknown package in user " + userId + ": " + packageName, e); diff --git a/apex/jobscheduler/framework/java/android/os/DeviceIdleManager.java b/apex/jobscheduler/framework/java/android/os/DeviceIdleManager.java index ada562ecebc6..752c36e53bf9 100644 --- a/apex/jobscheduler/framework/java/android/os/DeviceIdleManager.java +++ b/apex/jobscheduler/framework/java/android/os/DeviceIdleManager.java @@ -29,7 +29,6 @@ import android.content.Context; * * @hide */ -@TestApi @SystemApi @SystemService(Context.DEVICE_IDLE_CONTROLLER) public class DeviceIdleManager { diff --git a/apex/jobscheduler/framework/java/android/os/PowerWhitelistManager.java b/apex/jobscheduler/framework/java/android/os/PowerWhitelistManager.java index 16dcd0674809..ab8722286efc 100644 --- a/apex/jobscheduler/framework/java/android/os/PowerWhitelistManager.java +++ b/apex/jobscheduler/framework/java/android/os/PowerWhitelistManager.java @@ -21,7 +21,6 @@ import android.annotation.NonNull; import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.annotation.SystemService; -import android.annotation.TestApi; import android.content.Context; import java.lang.annotation.Retention; @@ -39,7 +38,6 @@ import java.util.List; * @hide */ @SystemApi -@TestApi @SystemService(Context.POWER_WHITELIST_MANAGER) public class PowerWhitelistManager { private final Context mContext; diff --git a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java index 1a81587990f2..81f22fe36ea1 100644 --- a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java +++ b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java @@ -2179,7 +2179,7 @@ public class DeviceIdleController extends SystemService if (getContext().getResources().getBoolean( com.android.internal.R.bool.config_autoPowerModePrefetchLocation)) { mLocationRequest = new LocationRequest.Builder(/*intervalMillis=*/ 0) - .setQuality(LocationRequest.ACCURACY_FINE) + .setQuality(LocationRequest.QUALITY_HIGH_ACCURACY) .setMaxUpdates(1) .build(); } diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/Alarm.java b/apex/jobscheduler/service/java/com/android/server/alarm/Alarm.java index f672e4b711c4..45ea23321d15 100644 --- a/apex/jobscheduler/service/java/com/android/server/alarm/Alarm.java +++ b/apex/jobscheduler/service/java/com/android/server/alarm/Alarm.java @@ -16,10 +16,13 @@ package com.android.server.alarm; +import static android.app.AlarmManager.ELAPSED_REALTIME; import static android.app.AlarmManager.ELAPSED_REALTIME_WAKEUP; import static android.app.AlarmManager.RTC; import static android.app.AlarmManager.RTC_WAKEUP; +import static com.android.server.alarm.AlarmManagerService.clampPositive; + import android.app.AlarmManager; import android.app.IAlarmListener; import android.app.PendingIntent; @@ -32,8 +35,28 @@ import java.io.PrintWriter; import java.text.SimpleDateFormat; import java.util.Date; +/** + * Class to describe an alarm that is used to the set the kernel timer that returns when the timer + * expires. The timer will wake up the device if the alarm is a "wakeup" alarm. + */ class Alarm { + private static final int NUM_POLICIES = 2; + /** + * Index used to store the time the alarm was requested to expire. To be used with + * {@link #setPolicyElapsed(int, long)} + */ + public static final int REQUESTER_POLICY_INDEX = 0; + /** + * Index used to store the earliest time the alarm can expire based on app-standby policy. + * To be used with {@link #setPolicyElapsed(int, long)} + */ + public static final int APP_STANDBY_POLICY_INDEX = 1; + public final int type; + /** + * The original trigger time supplied by the caller. This can be in the elapsed or rtc time base + * depending on the type of this alarm + */ public final long origWhen; public final boolean wakeup; public final PendingIntent operation; @@ -47,42 +70,40 @@ class Alarm { public final int creatorUid; public final String packageName; public final String sourcePackage; + public final long windowLength; + public final long repeatInterval; public int count; - public long when; - public long windowLength; - public long whenElapsed; // 'when' in the elapsed time base - public long maxWhenElapsed; // also in the elapsed time base - // Expected alarm expiry time before app standby deferring is applied. - public long expectedWhenElapsed; - public long expectedMaxWhenElapsed; - public long repeatInterval; + /** The earliest time this alarm is eligible to fire according to each policy */ + private long[] mPolicyWhenElapsed; + /** The ultimate delivery time to be used for this alarm */ + private long mWhenElapsed; + private long mMaxWhenElapsed; public AlarmManagerService.PriorityClass priorityClass; - Alarm(int _type, long _when, long _whenElapsed, long _windowLength, long _maxWhen, - long _interval, PendingIntent _op, IAlarmListener _rec, String _listenerTag, - WorkSource _ws, int _flags, AlarmManager.AlarmClockInfo _info, - int _uid, String _pkgName) { - type = _type; - origWhen = _when; - wakeup = _type == AlarmManager.ELAPSED_REALTIME_WAKEUP - || _type == AlarmManager.RTC_WAKEUP; - when = _when; - whenElapsed = _whenElapsed; - expectedWhenElapsed = _whenElapsed; - windowLength = _windowLength; - maxWhenElapsed = expectedMaxWhenElapsed = AlarmManagerService.clampPositive(_maxWhen); - repeatInterval = _interval; - operation = _op; - listener = _rec; - listenerTag = _listenerTag; - statsTag = makeTag(_op, _listenerTag, _type); - workSource = _ws; - flags = _flags; - alarmClock = _info; - uid = _uid; - packageName = _pkgName; + Alarm(int type, long when, long requestedWhenElapsed, long windowLength, long interval, + PendingIntent op, IAlarmListener rec, String listenerTag, WorkSource ws, int flags, + AlarmManager.AlarmClockInfo info, int uid, String pkgName) { + this.type = type; + origWhen = when; + wakeup = type == AlarmManager.ELAPSED_REALTIME_WAKEUP + || type == AlarmManager.RTC_WAKEUP; + mPolicyWhenElapsed = new long[NUM_POLICIES]; + mPolicyWhenElapsed[REQUESTER_POLICY_INDEX] = requestedWhenElapsed; + mWhenElapsed = requestedWhenElapsed; + this.windowLength = windowLength; + mMaxWhenElapsed = clampPositive(requestedWhenElapsed + windowLength); + repeatInterval = interval; + operation = op; + listener = rec; + this.listenerTag = listenerTag; + statsTag = makeTag(op, listenerTag, type); + workSource = ws; + this.flags = flags; + alarmClock = info; + this.uid = uid; + packageName = pkgName; sourcePackage = (operation != null) ? operation.getCreatorPackage() : packageName; - creatorUid = (operation != null) ? operation.getCreatorUid() : uid; + creatorUid = (operation != null) ? operation.getCreatorUid() : this.uid; } public static String makeTag(PendingIntent pi, String tag, int type) { @@ -91,13 +112,6 @@ class Alarm { return (pi != null) ? pi.getTag(alarmString) : (alarmString + tag); } - public AlarmManagerService.WakeupEvent makeWakeupEvent(long nowRTC) { - return new AlarmManagerService.WakeupEvent(nowRTC, creatorUid, - (operation != null) - ? operation.getIntent().getAction() - : ("<listener>:" + listenerTag)); - } - // Returns true if either matches public boolean matches(PendingIntent pi, IAlarmListener rec) { return (operation != null) @@ -109,6 +123,65 @@ class Alarm { return packageName.equals(sourcePackage); } + /** + * Get the earliest time this alarm is allowed to expire based on the given policy. + * + * @param policyIndex The index of the policy. One of [{@link #REQUESTER_POLICY_INDEX}, + * {@link #APP_STANDBY_POLICY_INDEX}]. + */ + public long getPolicyElapsed(int policyIndex) { + return mPolicyWhenElapsed[policyIndex]; + } + + /** + * Get the earliest time that this alarm should be delivered to the requesting app. + */ + public long getWhenElapsed() { + return mWhenElapsed; + } + + /** + * Get the latest time that this alarm should be delivered to the requesting app. Will be equal + * to {@link #getWhenElapsed()} in case this is an exact alarm. + */ + public long getMaxWhenElapsed() { + return mMaxWhenElapsed; + } + + /** + * Set the earliest time this alarm can expire based on the passed policy index. + * + * @return {@code true} if this change resulted in a change in the ultimate delivery time (or + * time window in the case of inexact alarms) of this alarm. + * @see #getWhenElapsed() + * @see #getMaxWhenElapsed() + * @see #getPolicyElapsed(int) + */ + public boolean setPolicyElapsed(int policyIndex, long policyElapsed) { + mPolicyWhenElapsed[policyIndex] = policyElapsed; + return updateWhenElapsed(); + } + + /** + * @return {@code true} if either {@link #mWhenElapsed} or {@link #mMaxWhenElapsed} changes + * due to this call. + */ + private boolean updateWhenElapsed() { + final long oldWhenElapsed = mWhenElapsed; + mWhenElapsed = 0; + for (int i = 0; i < NUM_POLICIES; i++) { + mWhenElapsed = Math.max(mWhenElapsed, mPolicyWhenElapsed[i]); + } + + final long oldMaxWhenElapsed = mMaxWhenElapsed; + // windowLength should always be >= 0 here. + final long maxRequestedElapsed = clampPositive( + mPolicyWhenElapsed[REQUESTER_POLICY_INDEX] + windowLength); + mMaxWhenElapsed = Math.max(maxRequestedElapsed, mWhenElapsed); + + return (oldWhenElapsed != mWhenElapsed) || (oldMaxWhenElapsed != mMaxWhenElapsed); + } + @Override public String toString() { StringBuilder sb = new StringBuilder(128); @@ -116,11 +189,11 @@ class Alarm { sb.append(Integer.toHexString(System.identityHashCode(this))); sb.append(" type "); sb.append(type); - sb.append(" when "); - sb.append(when); + sb.append(" origWhen "); + sb.append(origWhen); sb.append(" "); sb.append(" whenElapsed "); - sb.append(whenElapsed); + sb.append(getWhenElapsed()); sb.append(" "); sb.append(sourcePackage); sb.append('}'); @@ -136,30 +209,46 @@ class Alarm { dump(ipw, nowELAPSED, sdf); } + private static String policyIndexToString(int index) { + switch (index) { + case REQUESTER_POLICY_INDEX: + return "requester"; + case APP_STANDBY_POLICY_INDEX: + return "app_standby"; + default: + return "unknown"; + } + } + + public static String typeToString(int type) { + switch (type) { + case RTC: + return "RTC"; + case RTC_WAKEUP: + return "RTC_WAKEUP"; + case ELAPSED_REALTIME: + return "ELAPSED"; + case ELAPSED_REALTIME_WAKEUP: + return "ELAPSED_WAKEUP"; + default: + return "--unknown--"; + } + } + public void dump(IndentingPrintWriter ipw, long nowELAPSED, SimpleDateFormat sdf) { final boolean isRtc = (type == RTC || type == RTC_WAKEUP); ipw.print("tag="); ipw.println(statsTag); ipw.print("type="); - ipw.print(type); - ipw.print(" expectedWhenElapsed="); - TimeUtils.formatDuration(expectedWhenElapsed, nowELAPSED, ipw); - ipw.print(" expectedMaxWhenElapsed="); - TimeUtils.formatDuration(expectedMaxWhenElapsed, nowELAPSED, ipw); - ipw.print(" whenElapsed="); - TimeUtils.formatDuration(whenElapsed, nowELAPSED, ipw); - ipw.print(" maxWhenElapsed="); - TimeUtils.formatDuration(maxWhenElapsed, nowELAPSED, ipw); - ipw.print(" when="); + ipw.print(typeToString(type)); + ipw.print(" origWhen="); if (isRtc) { - ipw.print(sdf.format(new Date(when))); + ipw.print(sdf.format(new Date(origWhen))); } else { - TimeUtils.formatDuration(when, nowELAPSED, ipw); + TimeUtils.formatDuration(origWhen, nowELAPSED, ipw); } - ipw.println(); - - ipw.print("window="); + ipw.print(" window="); TimeUtils.formatDuration(windowLength, ipw); ipw.print(" repeatInterval="); ipw.print(repeatInterval); @@ -168,6 +257,19 @@ class Alarm { ipw.print(" flags=0x"); ipw.println(Integer.toHexString(flags)); + ipw.print("policyWhenElapsed:"); + for (int i = 0; i < NUM_POLICIES; i++) { + ipw.print(" " + policyIndexToString(i) + "="); + TimeUtils.formatDuration(mPolicyWhenElapsed[i], nowELAPSED, ipw); + } + ipw.println(); + + ipw.print("whenElapsed="); + TimeUtils.formatDuration(getWhenElapsed(), nowELAPSED, ipw); + ipw.print(" maxWhenElapsed="); + TimeUtils.formatDuration(mMaxWhenElapsed, nowELAPSED, ipw); + ipw.println(); + if (alarmClock != null) { ipw.println("Alarm clock:"); @@ -177,9 +279,10 @@ class Alarm { ipw.print(" showIntent="); ipw.println(alarmClock.getShowIntent()); } - ipw.print("operation="); - ipw.println(operation); - + if (operation != null) { + ipw.print("operation="); + ipw.println(operation); + } if (listener != null) { ipw.print("listener="); ipw.println(listener.asBinder()); @@ -191,7 +294,7 @@ class Alarm { proto.write(AlarmProto.TAG, statsTag); proto.write(AlarmProto.TYPE, type); - proto.write(AlarmProto.TIME_UNTIL_WHEN_ELAPSED_MS, whenElapsed - nowElapsed); + proto.write(AlarmProto.TIME_UNTIL_WHEN_ELAPSED_MS, getWhenElapsed() - nowElapsed); proto.write(AlarmProto.WINDOW_LENGTH_MS, windowLength); proto.write(AlarmProto.REPEAT_INTERVAL_MS, repeatInterval); proto.write(AlarmProto.COUNT, count); diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java index 05910a5b171e..82819dab0f69 100644 --- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java +++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java @@ -26,6 +26,9 @@ import static android.app.AlarmManager.RTC_WAKEUP; import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY; import static android.os.UserHandle.USER_SYSTEM; +import static com.android.server.alarm.Alarm.APP_STANDBY_POLICY_INDEX; +import static com.android.server.alarm.Alarm.REQUESTER_POLICY_INDEX; + import android.annotation.NonNull; import android.annotation.UserIdInt; import android.app.Activity; @@ -727,9 +730,9 @@ public class AlarmManagerService extends SystemService { } // within each class, sort by nominal delivery time - if (lhs.whenElapsed < rhs.whenElapsed) { + if (lhs.getWhenElapsed() < rhs.getWhenElapsed()) { return -1; - } else if (lhs.whenElapsed > rhs.whenElapsed) { + } else if (lhs.getWhenElapsed() > rhs.getWhenElapsed()) { return 1; } @@ -798,9 +801,12 @@ public class AlarmManagerService extends SystemService { this(context, new Injector(context)); } + private static boolean isRtc(int type) { + return (type == RTC || type == RTC_WAKEUP); + } + private long convertToElapsed(long when, int type) { - final boolean isRtc = (type == RTC || type == RTC_WAKEUP); - if (isRtc) { + if (isRtc(type)) { when -= mInjector.getCurrentTimeMillis() - mInjector.getElapsedRealtime(); } return when; @@ -823,13 +829,29 @@ public class AlarmManagerService extends SystemService { } // The RTC clock has moved arbitrarily, so we need to recalculate all the RTC alarm deliveries. - void reevaluateRtcAlarms(final long nowElapsed) { + void reevaluateRtcAlarms() { synchronized (mLock) { - final ArrayList<Alarm> rtcAlarms = mAlarmStore.remove(a -> (a.type == RTC - || a.type == RTC_WAKEUP)); - for (final Alarm a : rtcAlarms) { - restoreAlarmLocked(a, nowElapsed); - setImplLocked(a); + boolean changed = mAlarmStore.updateAlarmDeliveries(a -> { + if (!isRtc(a.type)) { + return false; + } + return restoreRequestedTime(a); + }); + + if (mNextWakeFromIdle != null && isRtc(mNextWakeFromIdle.type)) { + // The next wake from idle got updated due to the rtc time change, implying we need + // to update the time we have to come out of idle too. + changed |= mAlarmStore.updateAlarmDeliveries(a -> { + if (a != mPendingIdleUntil) { + return false; + } + return adjustIdleUntilTime(a); + }); + } + + if (changed) { + rescheduleKernelAlarmsLocked(); + // Only time shifted, so the next alarm clock will not change } } } @@ -844,7 +866,7 @@ public class AlarmManagerService extends SystemService { boolean reorderAlarmsBasedOnStandbyBuckets(ArraySet<Pair<String, Integer>> targetPackages) { final long start = mStatLogger.getTime(); - final boolean changed = mAlarmStore.recalculateAlarmDeliveries(a -> { + final boolean changed = mAlarmStore.updateAlarmDeliveries(a -> { final Pair<String, Integer> packageUser = Pair.create(a.sourcePackage, UserHandle.getUserId(a.creatorUid)); if (targetPackages != null && !targetPackages.contains(packageUser)) { @@ -857,23 +879,8 @@ public class AlarmManagerService extends SystemService { return changed; } - private void restoreAlarmLocked(Alarm a, long nowElapsed) { - a.when = a.origWhen; - long whenElapsed = convertToElapsed(a.when, a.type); - final long maxElapsed; - if (a.windowLength == AlarmManager.WINDOW_EXACT) { - // Exact - maxElapsed = whenElapsed; - } else { - // Not exact. Preserve any explicit window, otherwise recalculate - // the window based on the alarm's new futurity. Note that this - // reflects a policy of preferring timely to deferred delivery. - maxElapsed = (a.windowLength > 0) - ? clampPositive(whenElapsed + a.windowLength) - : maxTriggerTime(nowElapsed, whenElapsed, a.repeatInterval); - } - a.expectedWhenElapsed = a.whenElapsed = whenElapsed; - a.expectedMaxWhenElapsed = a.maxWhenElapsed = maxElapsed; + private boolean restoreRequestedTime(Alarm a) { + return a.setPolicyElapsed(REQUESTER_POLICY_INDEX, convertToElapsed(a.origWhen, a.type)); } static long clampPositive(long val) { @@ -973,14 +980,17 @@ public class AlarmManagerService extends SystemService { // Recurring alarms may have passed several alarm intervals while the // alarm was kept pending. Send the appropriate trigger count. if (alarm.repeatInterval > 0) { - alarm.count += (nowELAPSED - alarm.expectedWhenElapsed) / alarm.repeatInterval; + alarm.count += (nowELAPSED - alarm.getPolicyElapsed(REQUESTER_POLICY_INDEX)) + / alarm.repeatInterval; // Also schedule its next recurrence final long delta = alarm.count * alarm.repeatInterval; - final long nextElapsed = alarm.expectedWhenElapsed + delta; - setImplLocked(alarm.type, alarm.when + delta, nextElapsed, alarm.windowLength, - maxTriggerTime(nowELAPSED, nextElapsed, alarm.repeatInterval), - alarm.repeatInterval, alarm.operation, null, null, alarm.flags, - alarm.workSource, alarm.alarmClock, alarm.uid, alarm.packageName); + final long nextElapsed = alarm.getPolicyElapsed(REQUESTER_POLICY_INDEX) + delta; + final long nextMaxElapsed = maxTriggerTime(nowELAPSED, nextElapsed, + alarm.repeatInterval); + setImplLocked(alarm.type, alarm.origWhen + delta, nextElapsed, + nextMaxElapsed - nextElapsed, alarm.repeatInterval, alarm.operation, null, + null, alarm.flags, alarm.workSource, alarm.alarmClock, alarm.uid, + alarm.packageName); // Kernel alarms will be rescheduled as needed in setImplLocked } } @@ -1026,18 +1036,10 @@ public class AlarmManagerService extends SystemService { if (mPendingWhileIdleAlarms.size() > 0) { ArrayList<Alarm> alarms = mPendingWhileIdleAlarms; mPendingWhileIdleAlarms = new ArrayList<>(); - final long nowElapsed = mInjector.getElapsedRealtime(); for (int i = alarms.size() - 1; i >= 0; i--) { - Alarm a = alarms.get(i); - restoreAlarmLocked(a, nowElapsed); - setImplLocked(a); + setImplLocked(alarms.get(i)); } } - - // Reschedule everything. - rescheduleKernelAlarmsLocked(); - updateNextAlarmClockLocked(); - } static final class InFlight { @@ -1449,6 +1451,11 @@ public class AlarmManagerService extends SystemService { } } + if ((flags & AlarmManager.FLAG_IDLE_UNTIL) != 0) { + // Do not support windows for idle-until alarms. + windowLength = AlarmManager.WINDOW_EXACT; + } + // Sanity check the window length. This will catch people mistakenly // trying to pass an end-of-window timestamp rather than a duration. if (windowLength > AlarmManager.INTERVAL_HALF_DAY) { @@ -1515,17 +1522,17 @@ public class AlarmManagerService extends SystemService { Slog.w(TAG, errorMsg); throw new IllegalStateException(errorMsg); } - setImplLocked(type, triggerAtTime, triggerElapsed, windowLength, maxElapsed, - interval, operation, directReceiver, listenerTag, flags, workSource, - alarmClock, callingUid, callingPackage); + setImplLocked(type, triggerAtTime, triggerElapsed, windowLength, interval, operation, + directReceiver, listenerTag, flags, workSource, alarmClock, callingUid, + callingPackage); } } private void setImplLocked(int type, long when, long whenElapsed, long windowLength, - long maxWhen, long interval, PendingIntent operation, IAlarmListener directReceiver, + long interval, PendingIntent operation, IAlarmListener directReceiver, String listenerTag, int flags, WorkSource workSource, AlarmManager.AlarmClockInfo alarmClock, int callingUid, String callingPackage) { - Alarm a = new Alarm(type, when, whenElapsed, windowLength, maxWhen, interval, + final Alarm a = new Alarm(type, when, whenElapsed, windowLength, interval, operation, directReceiver, listenerTag, workSource, flags, alarmClock, callingUid, callingPackage); if (mActivityManagerInternal.isAppStartModeDisabled(callingUid, callingPackage)) { @@ -1560,72 +1567,55 @@ public class AlarmManagerService extends SystemService { } /** - * Adjusts the idle-until alarm delivery time based on the upcoming wake-from-idle alarm. + * An alarm with {@link AlarmManager#FLAG_IDLE_UNTIL} is a special alarm that will put the + * system into idle until it goes off. We need to pull it earlier if there are existing alarms + * that have requested to bring us out of idle at an earlier time. * * @param alarm The alarm to adjust * @return true if the alarm delivery time was updated. */ private boolean adjustIdleUntilTime(Alarm alarm) { - if ((alarm.flags & AlarmManager.FLAG_IDLE_UNTIL) != 0) { + if ((alarm.flags & AlarmManager.FLAG_IDLE_UNTIL) == 0) { return false; } - // This is a special alarm that will put the system into idle until it goes off. - // The caller has given the time they want this to happen at, however we need - // to pull that earlier if there are existing alarms that have requested to - // bring us out of idle at an earlier time. - if (mNextWakeFromIdle != null && alarm.whenElapsed > mNextWakeFromIdle.whenElapsed) { - alarm.when = alarm.whenElapsed = alarm.maxWhenElapsed = mNextWakeFromIdle.whenElapsed; + restoreRequestedTime(alarm); + long triggerBeforeFuzz = alarm.getPolicyElapsed(REQUESTER_POLICY_INDEX); + if (mNextWakeFromIdle != null && triggerBeforeFuzz > mNextWakeFromIdle.getWhenElapsed()) { + triggerBeforeFuzz = mNextWakeFromIdle.getWhenElapsed(); } // Add fuzz to make the alarm go off some time before the actual desired time. - final long nowElapsed = mInjector.getElapsedRealtime(); - final int fuzz = fuzzForDuration(alarm.whenElapsed - nowElapsed); + final int fuzz = fuzzForDuration(alarm.getWhenElapsed() - mInjector.getElapsedRealtime()); + final int delta; if (fuzz > 0) { if (mRandom == null) { mRandom = new Random(); } - final int delta = mRandom.nextInt(fuzz); - alarm.whenElapsed -= delta; - if (false) { - Slog.d(TAG, "Alarm when: " + alarm.whenElapsed); - Slog.d(TAG, "Delta until alarm: " + (alarm.whenElapsed - nowElapsed)); - Slog.d(TAG, "Applied fuzz: " + fuzz); - Slog.d(TAG, "Final delta: " + delta); - Slog.d(TAG, "Final when: " + alarm.whenElapsed); - } - alarm.when = alarm.maxWhenElapsed = alarm.whenElapsed; + delta = mRandom.nextInt(fuzz); + } else { + delta = 0; } + alarm.setPolicyElapsed(REQUESTER_POLICY_INDEX, triggerBeforeFuzz - delta); return true; } /** - * Adjusts the alarm delivery time based on the current app standby bucket. + * Adjusts the alarm's policy time for app_standby. * - * @param alarm The alarm to adjust - * @return true if the alarm delivery time was updated. + * @param alarm The alarm to update. + * @return {@code true} if the actual delivery time of the given alarm was updated due to + * adjustments made in this call. */ private boolean adjustDeliveryTimeBasedOnBucketLocked(Alarm alarm) { - if (isExemptFromAppStandby(alarm)) { - return false; - } - if (mAppStandbyParole) { - if (alarm.whenElapsed > alarm.expectedWhenElapsed) { - // We did defer this alarm earlier, restore original requirements - alarm.whenElapsed = alarm.expectedWhenElapsed; - alarm.maxWhenElapsed = alarm.expectedMaxWhenElapsed; - return true; - } - return false; + final long nowElapsed = mInjector.getElapsedRealtime(); + if (isExemptFromAppStandby(alarm) || mAppStandbyParole) { + return alarm.setPolicyElapsed(APP_STANDBY_POLICY_INDEX, nowElapsed); } - final long oldWhenElapsed = alarm.whenElapsed; - final long oldMaxWhenElapsed = alarm.maxWhenElapsed; final String sourcePackage = alarm.sourcePackage; final int sourceUserId = UserHandle.getUserId(alarm.creatorUid); final int standbyBucket = mUsageStatsManagerInternal.getAppStandbyBucket( - sourcePackage, sourceUserId, mInjector.getElapsedRealtime()); + sourcePackage, sourceUserId, nowElapsed); - // Quota deferring implementation: - boolean deferred = false; final int wakeupsInWindow = mAppWakeupHistory.getTotalWakeupsInWindow(sourcePackage, sourceUserId); if (standbyBucket == UsageStatsManager.STANDBY_BUCKET_RESTRICTED) { @@ -1635,14 +1625,9 @@ public class AlarmManagerService extends SystemService { if (wakeupsInWindow > 0) { final long lastWakeupTime = mAppWakeupHistory.getNthLastWakeupForPackage( sourcePackage, sourceUserId, mConstants.APP_STANDBY_RESTRICTED_QUOTA); - if (mInjector.getElapsedRealtime() - lastWakeupTime - < mConstants.APP_STANDBY_RESTRICTED_WINDOW) { - final long minElapsed = - lastWakeupTime + mConstants.APP_STANDBY_RESTRICTED_WINDOW; - if (alarm.expectedWhenElapsed < minElapsed) { - alarm.whenElapsed = alarm.maxWhenElapsed = minElapsed; - deferred = true; - } + if ((nowElapsed - lastWakeupTime) < mConstants.APP_STANDBY_RESTRICTED_WINDOW) { + return alarm.setPolicyElapsed(APP_STANDBY_POLICY_INDEX, + lastWakeupTime + mConstants.APP_STANDBY_RESTRICTED_WINDOW); } } } else { @@ -1651,7 +1636,7 @@ public class AlarmManagerService extends SystemService { final long minElapsed; if (quotaForBucket <= 0) { // Just keep deferring for a day till the quota changes - minElapsed = mInjector.getElapsedRealtime() + MILLIS_IN_DAY; + minElapsed = nowElapsed + MILLIS_IN_DAY; } else { // Suppose the quota for window was q, and the qth last delivery time for this // package was t(q) then the next delivery must be after t(q) + <window_size> @@ -1659,19 +1644,11 @@ public class AlarmManagerService extends SystemService { sourcePackage, sourceUserId, quotaForBucket); minElapsed = t + mConstants.APP_STANDBY_WINDOW; } - if (alarm.expectedWhenElapsed < minElapsed) { - alarm.whenElapsed = alarm.maxWhenElapsed = minElapsed; - deferred = true; - } + return alarm.setPolicyElapsed(APP_STANDBY_POLICY_INDEX, minElapsed); } } - if (!deferred) { - // Restore original requirements in case they were changed earlier. - alarm.whenElapsed = alarm.expectedWhenElapsed; - alarm.maxWhenElapsed = alarm.expectedMaxWhenElapsed; - } - - return (oldWhenElapsed != alarm.whenElapsed || oldMaxWhenElapsed != alarm.maxWhenElapsed); + // wakeupsInWindow are less than the permitted quota, hence no deferring is needed. + return alarm.setPolicyElapsed(APP_STANDBY_POLICY_INDEX, nowElapsed); } private static boolean isAllowedWhileIdle(Alarm a) { @@ -1691,7 +1668,7 @@ public class AlarmManagerService extends SystemService { ent.tag = a.operation.getTag(""); ent.op = "SET"; ent.elapsedRealtime = mInjector.getElapsedRealtime(); - ent.argRealtime = a.whenElapsed; + ent.argRealtime = a.getWhenElapsed(); mAllowWhileIdleDispatches.add(ent); if (mPendingIdleUntil == null) { IdleDispatchEntry ent2 = new IdleDispatchEntry(); @@ -1704,6 +1681,7 @@ public class AlarmManagerService extends SystemService { if ((mPendingIdleUntil != a) && (mPendingIdleUntil != null)) { Slog.wtfStack(TAG, "setImplLocked: idle until changed from " + mPendingIdleUntil + " to " + a); + mAlarmStore.remove(mPendingIdleUntil::equals); } mPendingIdleUntil = a; final ArrayList<Alarm> notAllowedWhileIdleAlarms = mAlarmStore.remove( @@ -1718,18 +1696,16 @@ public class AlarmManagerService extends SystemService { } } if ((a.flags & AlarmManager.FLAG_WAKE_FROM_IDLE) != 0) { - if (mNextWakeFromIdle == null || mNextWakeFromIdle.whenElapsed > a.whenElapsed) { + if (mNextWakeFromIdle == null || mNextWakeFromIdle.getWhenElapsed() + > a.getWhenElapsed()) { mNextWakeFromIdle = a; // If this wake from idle is earlier than whatever was previously scheduled, - // and we are currently idling, then we need to rebatch alarms in case the idle - // until time needs to be updated. + // and we are currently idling, then the idle-until time needs to be updated. if (mPendingIdleUntil != null) { - final long nowElapsed = mInjector.getElapsedRealtime(); - mAlarmStore.recalculateAlarmDeliveries(alarm -> { + mAlarmStore.updateAlarmDeliveries(alarm -> { if (alarm != mPendingIdleUntil) { return false; } - restoreAlarmLocked(alarm, nowElapsed); return adjustIdleUntilTime(alarm); }); } @@ -2563,7 +2539,7 @@ public class AlarmManagerService extends SystemService { long getNextWakeFromIdleTimeImpl() { synchronized (mLock) { - return mNextWakeFromIdle != null ? mNextWakeFromIdle.whenElapsed : Long.MAX_VALUE; + return mNextWakeFromIdle != null ? mNextWakeFromIdle.getWhenElapsed() : Long.MAX_VALUE; } } @@ -2784,12 +2760,11 @@ public class AlarmManagerService extends SystemService { restorePending = true; } if (mNextWakeFromIdle != null && mNextWakeFromIdle.matches(operation, directReceiver)) { - mNextWakeFromIdle = null; - mAlarmStore.recalculateAlarmDeliveries(alarm -> { + mNextWakeFromIdle = mAlarmStore.getNextWakeFromIdleAlarm(); + mAlarmStore.updateAlarmDeliveries(alarm -> { if (alarm != mPendingIdleUntil) { return false; } - restoreAlarmLocked(alarm, mInjector.getElapsedRealtime()); return adjustIdleUntilTime(alarm); }); } @@ -2834,15 +2809,14 @@ public class AlarmManagerService extends SystemService { mPendingBackgroundAlarms.removeAt(i); } } - // If we're currently keying off of this app's alarms for doze transitions, - // make sure to reset to other triggers. + // If we're currently using this app's alarms to come out of doze, + // make sure to reset to any remaining WAKE_FROM_IDLE alarms. if (mNextWakeFromIdle != null && mNextWakeFromIdle.uid == uid) { - mNextWakeFromIdle = null; - mAlarmStore.recalculateAlarmDeliveries(alarm -> { + mNextWakeFromIdle = mAlarmStore.getNextWakeFromIdleAlarm(); + mAlarmStore.updateAlarmDeliveries(alarm -> { if (alarm != mPendingIdleUntil) { return false; } - restoreAlarmLocked(alarm, mInjector.getElapsedRealtime()); return adjustIdleUntilTime(alarm); }); } @@ -2906,15 +2880,14 @@ public class AlarmManagerService extends SystemService { mPendingBackgroundAlarms.removeAt(i); } } - // If we're currently keying off of this app's alarms for doze transitions, - // make sure to reset to other triggers. + // If we're currently using this app's alarms to come out of doze, + // make sure to reset to any remaining WAKE_FROM_IDLE alarms. if (removedNextWakeFromIdle.value) { - mNextWakeFromIdle = null; - mAlarmStore.recalculateAlarmDeliveries(alarm -> { + mNextWakeFromIdle = mAlarmStore.getNextWakeFromIdleAlarm(); + mAlarmStore.updateAlarmDeliveries(alarm -> { if (alarm != mPendingIdleUntil) { return false; } - restoreAlarmLocked(alarm, mInjector.getElapsedRealtime()); return adjustIdleUntilTime(alarm); }); } @@ -3071,20 +3044,6 @@ public class AlarmManagerService extends SystemService { } } - private static final String labelForType(int type) { - switch (type) { - case RTC: - return "RTC"; - case RTC_WAKEUP: - return "RTC_WAKEUP"; - case ELAPSED_REALTIME: - return "ELAPSED"; - case ELAPSED_REALTIME_WAKEUP: - return "ELAPSED_WAKEUP"; - } - return "--unknown--"; - } - private static final void dumpAlarmList(PrintWriter pw, ArrayList<Alarm> list, String prefix, long nowELAPSED, SimpleDateFormat sdf) { final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, prefix, prefix); @@ -3095,7 +3054,7 @@ public class AlarmManagerService extends SystemService { long nowELAPSED, SimpleDateFormat sdf) { for (int i = list.size() - 1; i >= 0; i--) { final Alarm a = list.get(i); - final String label = labelForType(a.type); + final String label = Alarm.typeToString(a.type); ipw.print(label); ipw.print(" #"); ipw.print(i); @@ -3125,6 +3084,9 @@ public class AlarmManagerService extends SystemService { } final String sourcePackage = alarm.sourcePackage; final int sourceUid = alarm.creatorUid; + if (UserHandle.isCore(sourceUid)) { + return false; + } return (mAppStateTracker != null) && mAppStateTracker.areAlarmsRestricted(sourceUid, sourcePackage, exemptOnBatterySaver); @@ -3169,11 +3131,7 @@ public class AlarmManagerService extends SystemService { // Whoops, it hasn't been long enough since the last ALLOW_WHILE_IDLE // alarm went off for this app. Reschedule the alarm to be in the // correct time period. - alarm.expectedWhenElapsed = alarm.whenElapsed = minTime; - if (alarm.maxWhenElapsed < minTime) { - alarm.maxWhenElapsed = minTime; - } - alarm.expectedMaxWhenElapsed = alarm.maxWhenElapsed; + alarm.setPolicyElapsed(REQUESTER_POLICY_INDEX, minTime); if (RECORD_DEVICE_IDLE_ALARMS) { IdleDispatchEntry ent = new IdleDispatchEntry(); ent.uid = alarm.uid; @@ -3213,12 +3171,11 @@ public class AlarmManagerService extends SystemService { restorePendingWhileIdleAlarmsLocked(); } if (mNextWakeFromIdle == alarm) { - mNextWakeFromIdle = null; - mAlarmStore.recalculateAlarmDeliveries(a -> { + mNextWakeFromIdle = mAlarmStore.getNextWakeFromIdleAlarm(); + mAlarmStore.updateAlarmDeliveries(a -> { if (a != mPendingIdleUntil) { return false; } - restoreAlarmLocked(a, nowELAPSED); return adjustIdleUntilTime(a); }); } @@ -3228,14 +3185,17 @@ public class AlarmManagerService extends SystemService { if (alarm.repeatInterval > 0) { // this adjustment will be zero if we're late by // less than one full repeat interval - alarm.count += (nowELAPSED - alarm.expectedWhenElapsed) / alarm.repeatInterval; + alarm.count += (nowELAPSED - alarm.getPolicyElapsed(REQUESTER_POLICY_INDEX)) + / alarm.repeatInterval; // Also schedule its next recurrence final long delta = alarm.count * alarm.repeatInterval; - final long nextElapsed = alarm.expectedWhenElapsed + delta; - setImplLocked(alarm.type, alarm.when + delta, nextElapsed, alarm.windowLength, - maxTriggerTime(nowELAPSED, nextElapsed, alarm.repeatInterval), - alarm.repeatInterval, alarm.operation, null, null, alarm.flags, - alarm.workSource, alarm.alarmClock, alarm.uid, alarm.packageName); + final long nextElapsed = alarm.getPolicyElapsed(REQUESTER_POLICY_INDEX) + delta; + final long nextMaxElapsed = maxTriggerTime(nowELAPSED, nextElapsed, + alarm.repeatInterval); + setImplLocked(alarm.type, alarm.origWhen + delta, nextElapsed, + nextMaxElapsed - nextElapsed, alarm.repeatInterval, alarm.operation, null, + null, alarm.flags, alarm.workSource, alarm.alarmClock, alarm.uid, + alarm.packageName); } if (alarm.wakeup) { @@ -3277,7 +3237,7 @@ public class AlarmManagerService extends SystemService { } } - static int fuzzForDuration(long duration) { + int fuzzForDuration(long duration) { if (duration < 15 * 60 * 1000) { // If the duration until the time is less than 15 minutes, the maximum fuzz // is the duration. @@ -3480,7 +3440,7 @@ public class AlarmManagerService extends SystemService { FrameworkStatsLog.write(FrameworkStatsLog.WALL_CLOCK_TIME_SHIFTED, nowRTC); removeImpl(null, mTimeTickTrigger); removeImpl(mDateChangeSender, null); - reevaluateRtcAlarms(nowELAPSED); + reevaluateRtcAlarms(); mClockReceiver.scheduleTimeTickEvent(); mClockReceiver.scheduleDateChangedEvent(); synchronized (mLock) { diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmStore.java b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmStore.java index 9fdbb8bbffc7..7a846b9b82db 100644 --- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmStore.java +++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmStore.java @@ -48,6 +48,15 @@ public interface AlarmStore { ArrayList<Alarm> remove(Predicate<Alarm> whichAlarms); /** + * Gets the earliest alarm with the flag {@link android.app.AlarmManager#FLAG_WAKE_FROM_IDLE} + * based on {@link Alarm#getWhenElapsed()}. + * + * @return An alarm object matching the description above or {@code null} if no such alarm was + * found. + */ + Alarm getNextWakeFromIdleAlarm(); + + /** * Returns the total number of alarms in this store. */ int size(); @@ -71,7 +80,7 @@ public interface AlarmStore { /** * Removes all alarms that are pending delivery at the given time. * - * @param nowElapsed The time at which delivery eligibility is evaluated. + * @param nowElapsed The time at which delivery eligibility is evaluated. * @return The list of alarms pending at the given time. */ ArrayList<Alarm> removePendingAlarms(long nowElapsed); @@ -82,7 +91,7 @@ public interface AlarmStore { * * @return {@code true} if any of the alarm deliveries changed due to this call. */ - boolean recalculateAlarmDeliveries(AlarmDeliveryCalculator deliveryCalculator); + boolean updateAlarmDeliveries(AlarmDeliveryCalculator deliveryCalculator); /** * Returns all the alarms in the form of a list. @@ -97,6 +106,7 @@ public interface AlarmStore { * Primary useful for debugging. Can be called from the * {@link android.os.Binder#dump(FileDescriptor PrintWriter, String[]) dump} method of the * caller. + * * @param ipw The {@link IndentingPrintWriter} to write to. * @param nowElapsed the time when the dump is requested in the * {@link SystemClock#elapsedRealtime() @@ -112,7 +122,7 @@ public interface AlarmStore { /** * A functional interface used to update the alarm. Used to describe the update in - * {@link #recalculateAlarmDeliveries(AlarmDeliveryCalculator)} + * {@link #updateAlarmDeliveries(AlarmDeliveryCalculator)} */ @FunctionalInterface interface AlarmDeliveryCalculator { @@ -125,3 +135,4 @@ public interface AlarmStore { boolean updateAlarmDelivery(Alarm a); } } + diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/BatchingAlarmStore.java b/apex/jobscheduler/service/java/com/android/server/alarm/BatchingAlarmStore.java index 91c0c0579c69..cbfe80bdce24 100644 --- a/apex/jobscheduler/service/java/com/android/server/alarm/BatchingAlarmStore.java +++ b/apex/jobscheduler/service/java/com/android/server/alarm/BatchingAlarmStore.java @@ -66,8 +66,8 @@ public class BatchingAlarmStore implements AlarmStore { }; private static final Comparator<Alarm> sIncreasingTimeOrder = (a1, a2) -> { - long when1 = a1.whenElapsed; - long when2 = a2.whenElapsed; + long when1 = a1.getWhenElapsed(); + long when2 = a2.getWhenElapsed(); if (when1 > when2) { return 1; } @@ -99,11 +99,28 @@ public class BatchingAlarmStore implements AlarmStore { } if (!removed.isEmpty()) { mSize -= removed.size(); + // Not needed if only whole batches were removed, but keeping existing behavior. rebatchAllAlarms(); } return removed; } + @Override + public Alarm getNextWakeFromIdleAlarm() { + for (final Batch batch : mAlarmBatches) { + if ((batch.mFlags & AlarmManager.FLAG_WAKE_FROM_IDLE) == 0) { + continue; + } + for (int i = 0; i < batch.size(); i++) { + final Alarm a = batch.get(i); + if ((a.flags & AlarmManager.FLAG_WAKE_FROM_IDLE) != 0) { + return a; + } + } + } + return null; + } + private void rebatchAllAlarms() { final long start = mStatLogger.getTime(); final ArrayList<Batch> oldBatches = (ArrayList<Batch>) mAlarmBatches.clone(); @@ -157,7 +174,7 @@ public class BatchingAlarmStore implements AlarmStore { } @Override - public boolean recalculateAlarmDeliveries(AlarmDeliveryCalculator deliveryCalculator) { + public boolean updateAlarmDeliveries(AlarmDeliveryCalculator deliveryCalculator) { boolean changed = false; for (final Batch b : mAlarmBatches) { for (int i = 0; i < b.size(); i++) { @@ -204,7 +221,7 @@ public class BatchingAlarmStore implements AlarmStore { private void insertAndBatchAlarm(Alarm alarm) { final int whichBatch = ((alarm.flags & AlarmManager.FLAG_STANDALONE) != 0) ? -1 - : attemptCoalesce(alarm.whenElapsed, alarm.maxWhenElapsed); + : attemptCoalesce(alarm.getWhenElapsed(), alarm.getMaxWhenElapsed()); if (whichBatch < 0) { addBatch(mAlarmBatches, new Batch(alarm)); @@ -247,8 +264,8 @@ public class BatchingAlarmStore implements AlarmStore { final ArrayList<Alarm> mAlarms = new ArrayList<>(); Batch(Alarm seed) { - mStart = seed.whenElapsed; - mEnd = clampPositive(seed.maxWhenElapsed); + mStart = seed.getWhenElapsed(); + mEnd = clampPositive(seed.getMaxWhenElapsed()); mFlags = seed.flags; mAlarms.add(seed); } @@ -276,12 +293,12 @@ public class BatchingAlarmStore implements AlarmStore { if (DEBUG_BATCH) { Slog.v(TAG, "Adding " + alarm + " to " + this); } - if (alarm.whenElapsed > mStart) { - mStart = alarm.whenElapsed; + if (alarm.getWhenElapsed() > mStart) { + mStart = alarm.getWhenElapsed(); newStart = true; } - if (alarm.maxWhenElapsed < mEnd) { - mEnd = alarm.maxWhenElapsed; + if (alarm.getMaxWhenElapsed() < mEnd) { + mEnd = alarm.getMaxWhenElapsed(); } mFlags |= alarm.flags; @@ -309,11 +326,11 @@ public class BatchingAlarmStore implements AlarmStore { Slog.wtf(TAG, "Removed TIME_TICK alarm"); } } else { - if (alarm.whenElapsed > newStart) { - newStart = alarm.whenElapsed; + if (alarm.getWhenElapsed() > newStart) { + newStart = alarm.getWhenElapsed(); } - if (alarm.maxWhenElapsed < newEnd) { - newEnd = alarm.maxWhenElapsed; + if (alarm.getMaxWhenElapsed() < newEnd) { + newEnd = alarm.getMaxWhenElapsed(); } newFlags |= alarm.flags; i++; diff --git a/api/current.txt b/api/current.txt index e2adbc440ace..ce82b5ec8cba 100644 --- a/api/current.txt +++ b/api/current.txt @@ -1613,6 +1613,7 @@ package android { field public static final int windowHideAnimation = 16842935; // 0x10100b7 field public static final int windowIsFloating = 16842839; // 0x1010057 field public static final int windowIsTranslucent = 16842840; // 0x1010058 + field public static final int windowLayoutAffinity = 16844313; // 0x1010619 field public static final int windowLayoutInDisplayCutoutMode = 16844166; // 0x1010586 field public static final int windowLightNavigationBar = 16844140; // 0x101056c field public static final int windowLightStatusBar = 16844000; // 0x10104e0 @@ -2850,6 +2851,7 @@ package android.accessibilityservice { method public int describeContents(); method public int getDisplayId(); method public int getGestureId(); + method @NonNull public java.util.List<android.view.MotionEvent> getMotionEvents(); method public void writeToParcel(@NonNull android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.accessibilityservice.AccessibilityGestureEvent> CREATOR; } @@ -2889,6 +2891,7 @@ package android.accessibilityservice { field public static final int GESTURE_2_FINGER_DOUBLE_TAP = 20; // 0x14 field public static final int GESTURE_2_FINGER_DOUBLE_TAP_AND_HOLD = 40; // 0x28 field public static final int GESTURE_2_FINGER_SINGLE_TAP = 19; // 0x13 + field public static final int GESTURE_2_FINGER_SINGLE_TAP_AND_HOLD = 43; // 0x2b field public static final int GESTURE_2_FINGER_SWIPE_DOWN = 26; // 0x1a field public static final int GESTURE_2_FINGER_SWIPE_LEFT = 27; // 0x1b field public static final int GESTURE_2_FINGER_SWIPE_RIGHT = 28; // 0x1c @@ -2897,11 +2900,13 @@ package android.accessibilityservice { field public static final int GESTURE_3_FINGER_DOUBLE_TAP = 23; // 0x17 field public static final int GESTURE_3_FINGER_DOUBLE_TAP_AND_HOLD = 41; // 0x29 field public static final int GESTURE_3_FINGER_SINGLE_TAP = 22; // 0x16 + field public static final int GESTURE_3_FINGER_SINGLE_TAP_AND_HOLD = 44; // 0x2c field public static final int GESTURE_3_FINGER_SWIPE_DOWN = 30; // 0x1e field public static final int GESTURE_3_FINGER_SWIPE_LEFT = 31; // 0x1f field public static final int GESTURE_3_FINGER_SWIPE_RIGHT = 32; // 0x20 field public static final int GESTURE_3_FINGER_SWIPE_UP = 29; // 0x1d field public static final int GESTURE_3_FINGER_TRIPLE_TAP = 24; // 0x18 + field public static final int GESTURE_3_FINGER_TRIPLE_TAP_AND_HOLD = 45; // 0x2d field public static final int GESTURE_4_FINGER_DOUBLE_TAP = 38; // 0x26 field public static final int GESTURE_4_FINGER_DOUBLE_TAP_AND_HOLD = 42; // 0x2a field public static final int GESTURE_4_FINGER_SINGLE_TAP = 37; // 0x25 @@ -2928,6 +2933,7 @@ package android.accessibilityservice { field public static final int GESTURE_SWIPE_UP_AND_DOWN = 7; // 0x7 field public static final int GESTURE_SWIPE_UP_AND_LEFT = 13; // 0xd field public static final int GESTURE_SWIPE_UP_AND_RIGHT = 14; // 0xe + field public static final int GESTURE_UNKNOWN = 0; // 0x0 field public static final int GLOBAL_ACTION_ACCESSIBILITY_ALL_APPS = 14; // 0xe field public static final int GLOBAL_ACTION_ACCESSIBILITY_BUTTON = 11; // 0xb field public static final int GLOBAL_ACTION_ACCESSIBILITY_BUTTON_CHOOSER = 12; // 0xc @@ -3043,6 +3049,7 @@ package android.accessibilityservice { field public static final int FLAG_REQUEST_SHORTCUT_WARNING_DIALOG_SPOKEN_FEEDBACK = 1024; // 0x400 field public static final int FLAG_REQUEST_TOUCH_EXPLORATION_MODE = 4; // 0x4 field public static final int FLAG_RETRIEVE_INTERACTIVE_WINDOWS = 64; // 0x40 + field public static final int FLAG_SEND_MOTION_EVENTS = 16384; // 0x4000 field public static final int FLAG_SERVICE_HANDLES_DOUBLE_TAP = 2048; // 0x800 field public int eventTypes; field public int feedbackType; @@ -4898,7 +4905,7 @@ package android.app { @Deprecated public class Fragment implements android.content.ComponentCallbacks2 android.view.View.OnCreateContextMenuListener { ctor @Deprecated public Fragment(); method @Deprecated public void dump(String, java.io.FileDescriptor, java.io.PrintWriter, String[]); - method @Deprecated public final boolean equals(Object); + method @Deprecated public final boolean equals(@Nullable Object); method @Deprecated public final android.app.Activity getActivity(); method @Deprecated public boolean getAllowEnterTransitionOverlap(); method @Deprecated public boolean getAllowReturnTransitionOverlap(); @@ -10664,12 +10671,15 @@ package android.content { field public static final String ACTION_PACKAGE_CHANGED = "android.intent.action.PACKAGE_CHANGED"; field public static final String ACTION_PACKAGE_DATA_CLEARED = "android.intent.action.PACKAGE_DATA_CLEARED"; field public static final String ACTION_PACKAGE_FIRST_LAUNCH = "android.intent.action.PACKAGE_FIRST_LAUNCH"; + field public static final String ACTION_PACKAGE_FULLY_LOADED = "android.intent.action.PACKAGE_FULLY_LOADED"; field public static final String ACTION_PACKAGE_FULLY_REMOVED = "android.intent.action.PACKAGE_FULLY_REMOVED"; field @Deprecated public static final String ACTION_PACKAGE_INSTALL = "android.intent.action.PACKAGE_INSTALL"; field public static final String ACTION_PACKAGE_NEEDS_VERIFICATION = "android.intent.action.PACKAGE_NEEDS_VERIFICATION"; field public static final String ACTION_PACKAGE_REMOVED = "android.intent.action.PACKAGE_REMOVED"; field public static final String ACTION_PACKAGE_REPLACED = "android.intent.action.PACKAGE_REPLACED"; field public static final String ACTION_PACKAGE_RESTARTED = "android.intent.action.PACKAGE_RESTARTED"; + field public static final String ACTION_PACKAGE_STARTABLE = "android.intent.action.PACKAGE_STARTABLE"; + field public static final String ACTION_PACKAGE_UNSTARTABLE = "android.intent.action.PACKAGE_UNSTARTABLE"; field public static final String ACTION_PACKAGE_VERIFIED = "android.intent.action.PACKAGE_VERIFIED"; field public static final String ACTION_PASTE = "android.intent.action.PASTE"; field public static final String ACTION_PICK = "android.intent.action.PICK"; @@ -10834,6 +10844,7 @@ package android.content { field public static final String EXTRA_TIMEZONE = "time-zone"; field public static final String EXTRA_TITLE = "android.intent.extra.TITLE"; field public static final String EXTRA_UID = "android.intent.extra.UID"; + field public static final String EXTRA_UNSTARTABLE_REASON = "android.intent.extra.UNSTARTABLE_REASON"; field public static final String EXTRA_USER = "android.intent.extra.USER"; field public static final int FILL_IN_ACTION = 1; // 0x1 field public static final int FILL_IN_CATEGORIES = 4; // 0x4 @@ -12293,6 +12304,9 @@ package android.content.pm { field public static final int SYNCHRONOUS = 2; // 0x2 field @Nullable public static final java.util.List<java.security.cert.Certificate> TRUST_ALL; field @NonNull public static final java.util.List<java.security.cert.Certificate> TRUST_NONE; + field public static final int UNSTARTABLE_REASON_CONNECTION_ERROR = 1; // 0x1 + field public static final int UNSTARTABLE_REASON_INSUFFICIENT_STORAGE = 2; // 0x2 + field public static final int UNSTARTABLE_REASON_UNKNOWN = 0; // 0x0 field public static final int VERIFICATION_ALLOW = 1; // 0x1 field public static final int VERIFICATION_REJECT = -1; // 0xffffffff field public static final int VERSION_CODE_HIGHEST = -1; // 0xffffffff @@ -13063,38 +13077,38 @@ package android.database { public interface Cursor extends java.io.Closeable { method public void close(); - method public void copyStringToBuffer(int, android.database.CharArrayBuffer); + method public void copyStringToBuffer(@IntRange(from=0) int, android.database.CharArrayBuffer); method @Deprecated public void deactivate(); - method public byte[] getBlob(int); - method public int getColumnCount(); - method public int getColumnIndex(String); - method public int getColumnIndexOrThrow(String) throws java.lang.IllegalArgumentException; - method public String getColumnName(int); + method public byte[] getBlob(@IntRange(from=0) int); + method @IntRange(from=0) public int getColumnCount(); + method @IntRange(from=0xffffffff) public int getColumnIndex(String); + method @IntRange(from=0) public int getColumnIndexOrThrow(String) throws java.lang.IllegalArgumentException; + method public String getColumnName(@IntRange(from=0) int); method public String[] getColumnNames(); - method public int getCount(); - method public double getDouble(int); + method @IntRange(from=0) public int getCount(); + method public double getDouble(@IntRange(from=0) int); method public android.os.Bundle getExtras(); - method public float getFloat(int); - method public int getInt(int); - method public long getLong(int); + method public float getFloat(@IntRange(from=0) int); + method public int getInt(@IntRange(from=0) int); + method public long getLong(@IntRange(from=0) int); method public android.net.Uri getNotificationUri(); method @Nullable public default java.util.List<android.net.Uri> getNotificationUris(); - method public int getPosition(); - method public short getShort(int); - method public String getString(int); - method public int getType(int); + method @IntRange(from=0xffffffff) public int getPosition(); + method public short getShort(@IntRange(from=0) int); + method public String getString(@IntRange(from=0) int); + method public int getType(@IntRange(from=0) int); method public boolean getWantsAllOnMoveCalls(); method public boolean isAfterLast(); method public boolean isBeforeFirst(); method public boolean isClosed(); method public boolean isFirst(); method public boolean isLast(); - method public boolean isNull(int); + method public boolean isNull(@IntRange(from=0) int); method public boolean move(int); method public boolean moveToFirst(); method public boolean moveToLast(); method public boolean moveToNext(); - method public boolean moveToPosition(int); + method public boolean moveToPosition(@IntRange(from=0xffffffff) int); method public boolean moveToPrevious(); method public void registerContentObserver(android.database.ContentObserver); method public void registerDataSetObserver(android.database.DataSetObserver); @@ -13136,33 +13150,33 @@ package android.database { ctor @Deprecated public CursorWindow(boolean); method public boolean allocRow(); method public void clear(); - method public void copyStringToBuffer(int, int, android.database.CharArrayBuffer); + method public void copyStringToBuffer(@IntRange(from=0) int, @IntRange(from=0) int, android.database.CharArrayBuffer); method public int describeContents(); method public void freeLastRow(); - method public byte[] getBlob(int, int); - method public double getDouble(int, int); - method public float getFloat(int, int); - method public int getInt(int, int); - method public long getLong(int, int); - method public int getNumRows(); - method public short getShort(int, int); - method public int getStartPosition(); - method public String getString(int, int); - method public int getType(int, int); - method @Deprecated public boolean isBlob(int, int); - method @Deprecated public boolean isFloat(int, int); - method @Deprecated public boolean isLong(int, int); - method @Deprecated public boolean isNull(int, int); - method @Deprecated public boolean isString(int, int); + method public byte[] getBlob(@IntRange(from=0) int, @IntRange(from=0) int); + method public double getDouble(@IntRange(from=0) int, @IntRange(from=0) int); + method public float getFloat(@IntRange(from=0) int, @IntRange(from=0) int); + method public int getInt(@IntRange(from=0) int, @IntRange(from=0) int); + method public long getLong(@IntRange(from=0) int, @IntRange(from=0) int); + method @IntRange(from=0) public int getNumRows(); + method public short getShort(@IntRange(from=0) int, @IntRange(from=0) int); + method @IntRange(from=0) public int getStartPosition(); + method public String getString(@IntRange(from=0) int, @IntRange(from=0) int); + method public int getType(@IntRange(from=0) int, @IntRange(from=0) int); + method @Deprecated public boolean isBlob(@IntRange(from=0) int, @IntRange(from=0) int); + method @Deprecated public boolean isFloat(@IntRange(from=0) int, @IntRange(from=0) int); + method @Deprecated public boolean isLong(@IntRange(from=0) int, @IntRange(from=0) int); + method @Deprecated public boolean isNull(@IntRange(from=0) int, @IntRange(from=0) int); + method @Deprecated public boolean isString(@IntRange(from=0) int, @IntRange(from=0) int); method public static android.database.CursorWindow newFromParcel(android.os.Parcel); method protected void onAllReferencesReleased(); - method public boolean putBlob(byte[], int, int); - method public boolean putDouble(double, int, int); - method public boolean putLong(long, int, int); - method public boolean putNull(int, int); - method public boolean putString(String, int, int); - method public boolean setNumColumns(int); - method public void setStartPosition(int); + method public boolean putBlob(byte[], @IntRange(from=0) int, @IntRange(from=0) int); + method public boolean putDouble(double, @IntRange(from=0) int, @IntRange(from=0) int); + method public boolean putLong(long, @IntRange(from=0) int, @IntRange(from=0) int); + method public boolean putNull(@IntRange(from=0) int, @IntRange(from=0) int); + method public boolean putString(String, @IntRange(from=0) int, @IntRange(from=0) int); + method public boolean setNumColumns(@IntRange(from=0) int); + method public void setStartPosition(@IntRange(from=0) int); method public void writeToParcel(android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.database.CursorWindow> CREATOR; } @@ -16522,23 +16536,6 @@ package android.graphics.pdf { package android.graphics.text { - public class GlyphStyle { - ctor public GlyphStyle(@ColorInt int, @FloatRange(from=0) float, @FloatRange(from=0) float, @FloatRange(from=0) float, int); - ctor public GlyphStyle(@NonNull android.graphics.Paint); - method public void applyToPaint(@NonNull android.graphics.Paint); - method @ColorInt public int getColor(); - method public int getFlags(); - method @FloatRange(from=0) public float getFontSize(); - method @FloatRange(from=0) public float getScaleX(); - method @FloatRange(from=0) public float getSkewX(); - method public void setColor(@ColorInt int); - method public void setFlags(int); - method public void setFontSize(@FloatRange(from=0) float); - method public void setFromPaint(@NonNull android.graphics.Paint); - method public void setScaleX(@FloatRange(from=0) float); - method public void setSkewX(@FloatRange(from=0) float); - } - public class LineBreaker { method @NonNull public android.graphics.text.LineBreaker.Result computeLineBreaks(@NonNull android.graphics.text.MeasuredText, @NonNull android.graphics.text.LineBreaker.ParagraphConstraints, @IntRange(from=0) int); field public static final int BREAK_STRATEGY_BALANCED = 2; // 0x2 @@ -16600,20 +16597,19 @@ package android.graphics.text { } public final class PositionedGlyphs { + method public float getAdvance(); method public float getAscent(); method public float getDescent(); method @NonNull public android.graphics.fonts.Font getFont(@IntRange(from=0) int); method @IntRange(from=0) public int getGlyphId(@IntRange(from=0) int); - method public float getOriginX(); - method public float getOriginY(); - method public float getPositionX(@IntRange(from=0) int); - method public float getPositionY(@IntRange(from=0) int); - method @NonNull public android.graphics.text.GlyphStyle getStyle(); - method public float getTotalAdvance(); + method public float getGlyphX(@IntRange(from=0) int); + method public float getGlyphY(@IntRange(from=0) int); + method public float getOffsetX(); + method public float getOffsetY(); method @IntRange(from=0) public int glyphCount(); } - public class TextShaper { + public class TextRunShaper { method @NonNull public static android.graphics.text.PositionedGlyphs shapeTextRun(@NonNull char[], int, int, int, int, float, float, boolean, @NonNull android.graphics.Paint); method @NonNull public static android.graphics.text.PositionedGlyphs shapeTextRun(@NonNull CharSequence, int, int, int, int, float, float, boolean, @NonNull android.graphics.Paint); } @@ -24089,9 +24085,13 @@ package android.location { method @IntRange(from=1, to=java.lang.Integer.MAX_VALUE) public int getMaxUpdates(); method @FloatRange(from=0, to=java.lang.Float.MAX_VALUE) public float getMinUpdateDistanceMeters(); method @IntRange(from=0) public long getMinUpdateIntervalMillis(); + method public int getQuality(); method public void writeToParcel(@NonNull android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.location.LocationRequest> CREATOR; field public static final long PASSIVE_INTERVAL = 9223372036854775807L; // 0x7fffffffffffffffL + field public static final int QUALITY_BALANCED_POWER_ACCURACY = 102; // 0x66 + field public static final int QUALITY_HIGH_ACCURACY = 100; // 0x64 + field public static final int QUALITY_LOW_POWER = 104; // 0x68 } public static final class LocationRequest.Builder { @@ -24104,6 +24104,7 @@ package android.location { method @NonNull public android.location.LocationRequest.Builder setMaxUpdates(@IntRange(from=1, to=java.lang.Integer.MAX_VALUE) int); method @NonNull public android.location.LocationRequest.Builder setMinUpdateDistanceMeters(@FloatRange(from=0, to=java.lang.Float.MAX_VALUE) float); method @NonNull public android.location.LocationRequest.Builder setMinUpdateIntervalMillis(@IntRange(from=0) long); + method @NonNull public android.location.LocationRequest.Builder setQuality(int); } public interface OnNmeaMessageListener { @@ -30254,9 +30255,12 @@ package android.net { method public int describeContents(); method @NonNull public byte[] getKey(); method @NonNull public String getName(); + method @NonNull public static java.util.Set<java.lang.String> getSupportedAlgorithms(); method public int getTruncationLengthBits(); method public void writeToParcel(android.os.Parcel, int); + field public static final String AUTH_AES_XCBC = "xcbc(aes)"; field public static final String AUTH_CRYPT_AES_GCM = "rfc4106(gcm(aes))"; + field public static final String AUTH_CRYPT_CHACHA20_POLY1305 = "rfc7539esp(chacha20,poly1305)"; field public static final String AUTH_HMAC_MD5 = "hmac(md5)"; field public static final String AUTH_HMAC_SHA1 = "hmac(sha1)"; field public static final String AUTH_HMAC_SHA256 = "hmac(sha256)"; @@ -30264,6 +30268,7 @@ package android.net { field public static final String AUTH_HMAC_SHA512 = "hmac(sha512)"; field @NonNull public static final android.os.Parcelable.Creator<android.net.IpSecAlgorithm> CREATOR; field public static final String CRYPT_AES_CBC = "cbc(aes)"; + field public static final String CRYPT_AES_CTR = "rfc3686(ctr(aes))"; } public final class IpSecManager { @@ -31851,6 +31856,7 @@ package android.net.wifi.aware { method public int getMaxServiceNameLength(); method public int getMaxServiceSpecificInfoLength(); method public int getSupportedCipherSuites(); + method public boolean isInstantCommunicationModeSupported(); method public void writeToParcel(android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.aware.Characteristics> CREATOR; field public static final int WIFI_AWARE_CIPHER_SUITE_NCS_SK_128 = 1; // 0x1 @@ -31872,7 +31878,7 @@ package android.net.wifi.aware { method public void onPublishStarted(@NonNull android.net.wifi.aware.PublishDiscoverySession); method public void onServiceDiscovered(android.net.wifi.aware.PeerHandle, byte[], java.util.List<byte[]>); method public void onServiceDiscoveredWithinRange(android.net.wifi.aware.PeerHandle, byte[], java.util.List<byte[]>, int); - method public void onServiceLost(@NonNull android.net.wifi.aware.PeerHandle); + method public void onServiceLost(@NonNull android.net.wifi.aware.PeerHandle, int); method public void onSessionConfigFailed(); method public void onSessionConfigUpdated(); method public void onSessionTerminated(); @@ -31949,9 +31955,12 @@ package android.net.wifi.aware { method public android.net.wifi.aware.Characteristics getCharacteristics(); method public boolean isAvailable(); method public boolean isDeviceAttached(); + method public boolean isInstantCommunicationModeEnabled(); field public static final String ACTION_WIFI_AWARE_STATE_CHANGED = "android.net.wifi.aware.action.WIFI_AWARE_STATE_CHANGED"; field public static final int WIFI_AWARE_DATA_PATH_ROLE_INITIATOR = 0; // 0x0 field public static final int WIFI_AWARE_DATA_PATH_ROLE_RESPONDER = 1; // 0x1 + field public static final int WIFI_AWARE_DISCOVERY_LOST_REASON_PEER_NOT_VISIBLE = 1; // 0x1 + field public static final int WIFI_AWARE_DISCOVERY_LOST_REASON_UNKNOWN = 0; // 0x0 } public final class WifiAwareNetworkInfo implements android.os.Parcelable android.net.TransportInfo { @@ -32091,13 +32100,13 @@ package android.net.wifi.hotspot2.pps { method public String getFriendlyName(); method @Nullable public long[] getMatchAllOis(); method @Nullable public long[] getMatchAnyOis(); - method @Nullable public String[] getOtherHomePartners(); + method @NonNull public java.util.Collection<java.lang.String> getOtherHomePartnersList(); method public long[] getRoamingConsortiumOis(); method public void setFqdn(String); method public void setFriendlyName(String); method public void setMatchAllOis(@Nullable long[]); method public void setMatchAnyOis(@Nullable long[]); - method public void setOtherHomePartners(@Nullable String[]); + method public void setOtherHomePartnersList(@NonNull java.util.Collection<java.lang.String>); method public void setRoamingConsortiumOis(long[]); method public void writeToParcel(android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.hotspot2.pps.HomeSp> CREATOR; @@ -43296,8 +43305,16 @@ package android.service.autofill { method @NonNull public java.util.Map<android.view.autofill.AutofillId,android.service.autofill.FieldClassification> getFieldsClassification(); method @NonNull public java.util.Set<java.lang.String> getIgnoredDatasetIds(); method @NonNull public java.util.Map<android.view.autofill.AutofillId,java.util.Set<java.lang.String>> getManuallyEnteredField(); + method public int getNoSaveReason(); method @NonNull public java.util.Set<java.lang.String> getSelectedDatasetIds(); method public int getType(); + field public static final int NO_SAVE_REASON_DATASET_MATCH = 6; // 0x6 + field public static final int NO_SAVE_REASON_FIELD_VALIDATION_FAILED = 5; // 0x5 + field public static final int NO_SAVE_REASON_HAS_EMPTY_REQUIRED = 3; // 0x3 + field public static final int NO_SAVE_REASON_NONE = 0; // 0x0 + field public static final int NO_SAVE_REASON_NO_SAVE_INFO = 1; // 0x1 + field public static final int NO_SAVE_REASON_NO_VALUE_CHANGED = 4; // 0x4 + field public static final int NO_SAVE_REASON_WITH_DELAY_SAVE_FLAG = 2; // 0x2 field public static final int TYPE_AUTHENTICATION_SELECTED = 2; // 0x2 field public static final int TYPE_CONTEXT_COMMITTED = 4; // 0x4 field public static final int TYPE_DATASETS_SHOWN = 5; // 0x5 @@ -50056,10 +50073,6 @@ package android.text { method @NonNull public android.text.StaticLayout.Builder setUseLineSpacingFromFallbacks(boolean); } - public class StyledTextShaper { - method @NonNull public static java.util.List<android.graphics.text.PositionedGlyphs> shapeText(@NonNull CharSequence, int, int, @NonNull android.text.TextDirectionHeuristic, @NonNull android.text.TextPaint); - } - public interface TextDirectionHeuristic { method public boolean isRtl(char[], int, int); method public boolean isRtl(CharSequence, int, int); @@ -50089,6 +50102,14 @@ package android.text { field @Px public float underlineThickness; } + public class TextShaper { + method public static void shapeText(@NonNull CharSequence, @IntRange(from=0) int, @IntRange(from=0) int, @NonNull android.text.TextDirectionHeuristic, @NonNull android.text.TextPaint, @NonNull android.text.TextShaper.GlyphsConsumer); + } + + public static interface TextShaper.GlyphsConsumer { + method public void accept(@IntRange(from=0) int, @IntRange(from=0) int, @NonNull android.graphics.text.PositionedGlyphs, @NonNull android.text.TextPaint); + } + public class TextUtils { method @Deprecated public static CharSequence commaEllipsize(CharSequence, android.text.TextPaint, float, String, String); method public static CharSequence concat(java.lang.CharSequence...); @@ -53725,11 +53746,12 @@ package android.view { method @Nullable public android.net.Uri getLinkUri(); method public int getSource(); field public static final int FLAG_CONVERT_TO_PLAIN_TEXT = 1; // 0x1 - field public static final int SOURCE_AUTOFILL = 3; // 0x3 - field public static final int SOURCE_CLIPBOARD = 0; // 0x0 - field public static final int SOURCE_DRAG_AND_DROP = 2; // 0x2 - field public static final int SOURCE_INPUT_METHOD = 1; // 0x1 - field public static final int SOURCE_PROCESS_TEXT = 4; // 0x4 + field public static final int SOURCE_APP = 0; // 0x0 + field public static final int SOURCE_AUTOFILL = 4; // 0x4 + field public static final int SOURCE_CLIPBOARD = 1; // 0x1 + field public static final int SOURCE_DRAG_AND_DROP = 3; // 0x3 + field public static final int SOURCE_INPUT_METHOD = 2; // 0x2 + field public static final int SOURCE_PROCESS_TEXT = 5; // 0x5 } public static final class OnReceiveContentCallback.Payload.Builder { diff --git a/api/module-lib-current.txt b/api/module-lib-current.txt index dc92b52dc466..872f1f2c0591 100644 --- a/api/module-lib-current.txt +++ b/api/module-lib-current.txt @@ -1,10 +1,20 @@ // Signature format: 2.0 package android.app { + public class ActivityManager { + method @RequiresPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER) public void addHomeVisibilityListener(@NonNull java.util.concurrent.Executor, @NonNull android.app.HomeVisibilityListener); + method @RequiresPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER) public void removeHomeVisibilityListener(@NonNull android.app.HomeVisibilityListener); + } + public class AppOpsManager { field public static final String OPSTR_NO_ISOLATED_STORAGE = "android:no_isolated_storage"; } + public abstract class HomeVisibilityListener { + ctor public HomeVisibilityListener(); + method public abstract void onHomeVisibilityChanged(boolean); + } + public class NotificationManager { method public boolean hasEnabledNotificationListener(@NonNull String, @NonNull android.os.UserHandle); field public static final String ACTION_NOTIFICATION_LISTENER_ENABLED_CHANGED = "android.app.action.NOTIFICATION_LISTENER_ENABLED_CHANGED"; @@ -93,6 +103,10 @@ package android.media.session { field public static final int RESULT_MEDIA_KEY_NOT_HANDLED = 0; // 0x0 } + public final class PlaybackState implements android.os.Parcelable { + method public boolean isActiveState(); + } + } package android.net { @@ -143,8 +157,6 @@ package android.os { public interface Parcelable { method public default int getStability(); - field public static final int PARCELABLE_STABILITY_LOCAL = 0; // 0x0 - field public static final int PARCELABLE_STABILITY_VINTF = 1; // 0x1 } public class StatsFrameworkInitializer { @@ -178,6 +190,18 @@ package android.provider { } +package android.telephony { + + public abstract class CellSignalStrength { + method public static int getNumSignalStrengthLevels(); + } + + public class TelephonyManager { + method @NonNull public static int[] getAllNetworkTypes(); + } + +} + package android.util { public class AtomicFile { diff --git a/api/system-current.txt b/api/system-current.txt index 2e53058f96d2..b2f1cfe04638 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -122,6 +122,7 @@ package android { field public static final String MANAGE_FACTORY_RESET_PROTECTION = "android.permission.MANAGE_FACTORY_RESET_PROTECTION"; field public static final String MANAGE_IPSEC_TUNNELS = "android.permission.MANAGE_IPSEC_TUNNELS"; field public static final String MANAGE_MUSIC_RECOGNITION = "android.permission.MANAGE_MUSIC_RECOGNITION"; + field public static final String MANAGE_NOTIFICATION_LISTENERS = "android.permission.MANAGE_NOTIFICATION_LISTENERS"; field public static final String MANAGE_ONE_TIME_PERMISSION_SESSIONS = "android.permission.MANAGE_ONE_TIME_PERMISSION_SESSIONS"; field public static final String MANAGE_ROLE_HOLDERS = "android.permission.MANAGE_ROLE_HOLDERS"; field public static final String MANAGE_ROLLBACKS = "android.permission.MANAGE_ROLLBACKS"; @@ -224,6 +225,7 @@ package android { field public static final String SET_WALLPAPER_COMPONENT = "android.permission.SET_WALLPAPER_COMPONENT"; field public static final String SHOW_KEYGUARD_MESSAGE = "android.permission.SHOW_KEYGUARD_MESSAGE"; field public static final String SHUTDOWN = "android.permission.SHUTDOWN"; + field public static final String START_ACTIVITIES_FROM_BACKGROUND = "android.permission.START_ACTIVITIES_FROM_BACKGROUND"; field public static final String STATUS_BAR_SERVICE = "android.permission.STATUS_BAR_SERVICE"; field public static final String STOP_APP_SWITCHES = "android.permission.STOP_APP_SWITCHES"; field public static final String SUBSTITUTE_NOTIFICATION_APP_NAME = "android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME"; @@ -593,7 +595,7 @@ package android.app { public class BroadcastOptions { method public static android.app.BroadcastOptions makeBasic(); - method @RequiresPermission("android.permission.START_ACTIVITIES_FROM_BACKGROUND") public void setBackgroundActivityStartsAllowed(boolean); + method @RequiresPermission(android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND) public void setBackgroundActivityStartsAllowed(boolean); method public void setDontSendToRestrictedApps(boolean); method @RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST) public void setTemporaryAppWhitelistDuration(long); method public android.os.Bundle toBundle(); @@ -674,8 +676,10 @@ package android.app { public class NotificationManager { method @NonNull public java.util.List<java.lang.String> getAllowedAssistantAdjustments(); method @Nullable public android.content.ComponentName getAllowedNotificationAssistant(); + method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_NOTIFICATION_LISTENERS) public java.util.List<android.content.ComponentName> getEnabledNotificationListeners(); method public boolean isNotificationAssistantAccessGranted(@NonNull android.content.ComponentName); method public void setNotificationAssistantAccessGranted(@Nullable android.content.ComponentName, boolean); + method @RequiresPermission(android.Manifest.permission.MANAGE_NOTIFICATION_LISTENERS) public void setNotificationListenerAccessGranted(@NonNull android.content.ComponentName, boolean); field @RequiresPermission(android.Manifest.permission.STATUS_BAR_SERVICE) public static final String ACTION_CLOSE_NOTIFICATION_HANDLER_PANEL = "android.app.action.CLOSE_NOTIFICATION_HANDLER_PANEL"; field @RequiresPermission(android.Manifest.permission.STATUS_BAR_SERVICE) public static final String ACTION_OPEN_NOTIFICATION_HANDLER_PANEL = "android.app.action.OPEN_NOTIFICATION_HANDLER_PANEL"; field @RequiresPermission(android.Manifest.permission.STATUS_BAR_SERVICE) public static final String ACTION_TOGGLE_NOTIFICATION_HANDLER_PANEL = "android.app.action.TOGGLE_NOTIFICATION_HANDLER_PANEL"; @@ -1448,20 +1452,20 @@ package android.app.time { package android.app.usage { public final class CacheQuotaHint implements android.os.Parcelable { - ctor public CacheQuotaHint(android.app.usage.CacheQuotaHint.Builder); + ctor public CacheQuotaHint(@NonNull android.app.usage.CacheQuotaHint.Builder); method public int describeContents(); method public long getQuota(); method public int getUid(); - method public android.app.usage.UsageStats getUsageStats(); - method public String getVolumeUuid(); - method public void writeToParcel(android.os.Parcel, int); + method @Nullable public android.app.usage.UsageStats getUsageStats(); + method @Nullable public String getVolumeUuid(); + method public void writeToParcel(@NonNull android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.app.usage.CacheQuotaHint> CREATOR; field public static final long QUOTA_NOT_SET = -1L; // 0xffffffffffffffffL } public static final class CacheQuotaHint.Builder { ctor public CacheQuotaHint.Builder(); - ctor public CacheQuotaHint.Builder(android.app.usage.CacheQuotaHint); + ctor public CacheQuotaHint.Builder(@NonNull android.app.usage.CacheQuotaHint); method @NonNull public android.app.usage.CacheQuotaHint build(); method @NonNull public android.app.usage.CacheQuotaHint.Builder setQuota(long); method @NonNull public android.app.usage.CacheQuotaHint.Builder setUid(int); @@ -1799,6 +1803,7 @@ package android.content { field public static final String APP_PREDICTION_SERVICE = "app_prediction"; field public static final String BACKUP_SERVICE = "backup"; field public static final String BATTERY_STATS_SERVICE = "batterystats"; + field public static final int BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS = 1048576; // 0x100000 field public static final String BUGREPORT_SERVICE = "bugreport"; field public static final String CONTENT_SUGGESTIONS_SERVICE = "content_suggestions"; field public static final String CONTEXTHUB_SERVICE = "contexthub"; @@ -4171,7 +4176,6 @@ package android.location { method @Deprecated public long getInterval(); method @Deprecated public int getNumUpdates(); method @Deprecated @NonNull public String getProvider(); - method public int getQuality(); method @Deprecated public float getSmallestDisplacement(); method @NonNull public android.os.WorkSource getWorkSource(); method public boolean isHiddenFromAppOps(); @@ -4190,11 +4194,11 @@ package android.location { method @Deprecated @NonNull public android.location.LocationRequest setQuality(int); method @Deprecated @NonNull public android.location.LocationRequest setSmallestDisplacement(float); method @Deprecated public void setWorkSource(@Nullable android.os.WorkSource); - field public static final int ACCURACY_BLOCK = 102; // 0x66 - field public static final int ACCURACY_CITY = 104; // 0x68 - field public static final int ACCURACY_FINE = 100; // 0x64 - field public static final int POWER_HIGH = 203; // 0xcb - field public static final int POWER_LOW = 201; // 0xc9 + field @Deprecated public static final int ACCURACY_BLOCK = 102; // 0x66 + field @Deprecated public static final int ACCURACY_CITY = 104; // 0x68 + field @Deprecated public static final int ACCURACY_FINE = 100; // 0x64 + field @Deprecated public static final int POWER_HIGH = 203; // 0xcb + field @Deprecated public static final int POWER_LOW = 201; // 0xc9 field @Deprecated public static final int POWER_NONE = 200; // 0xc8 } @@ -4202,7 +4206,6 @@ package android.location { method @NonNull @RequiresPermission(android.Manifest.permission.UPDATE_APP_OPS_STATS) public android.location.LocationRequest.Builder setHiddenFromAppOps(boolean); method @NonNull @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public android.location.LocationRequest.Builder setLocationSettingsIgnored(boolean); method @NonNull @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public android.location.LocationRequest.Builder setLowPower(boolean); - method @NonNull public android.location.LocationRequest.Builder setQuality(int); method @NonNull @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public android.location.LocationRequest.Builder setWorkSource(@Nullable android.os.WorkSource); } @@ -4769,6 +4772,22 @@ package android.media.tv { field @NonNull public static final android.os.Parcelable.Creator<android.media.tv.DvbDeviceInfo> CREATOR; } + public final class TunedInfo implements android.os.Parcelable { + method public int describeContents(); + method public int getAppTag(); + method public int getAppType(); + method @Nullable public android.net.Uri getChannelUri(); + method @NonNull public String getInputId(); + method public boolean isForeground(); + method public boolean isRecordingSession(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field public static final int APP_TAG_SELF = 0; // 0x0 + field public static final int APP_TYPE_NON_SYSTEM = 3; // 0x3 + field public static final int APP_TYPE_SELF = 1; // 0x1 + field public static final int APP_TYPE_SYSTEM = 2; // 0x2 + field @NonNull public static final android.os.Parcelable.Creator<android.media.tv.TunedInfo> CREATOR; + } + public final class TvContentRatingSystemInfo implements android.os.Parcelable { method public static android.media.tv.TvContentRatingSystemInfo createTvContentRatingSystemInfo(int, android.content.pm.ApplicationInfo); method public int describeContents(); @@ -4884,6 +4903,7 @@ package android.media.tv { method @RequiresPermission(android.Manifest.permission.MODIFY_PARENTAL_CONTROLS) public void addBlockedRating(@NonNull android.media.tv.TvContentRating); method @RequiresPermission(android.Manifest.permission.CAPTURE_TV_INPUT) public boolean captureFrame(String, android.view.Surface, android.media.tv.TvStreamConfig); method @RequiresPermission(android.Manifest.permission.CAPTURE_TV_INPUT) public java.util.List<android.media.tv.TvStreamConfig> getAvailableTvStreamConfigList(String); + method @NonNull @RequiresPermission("com.android.providers.tv.permission.ACCESS_WATCHED_PROGRAMS") public java.util.List<android.media.tv.TunedInfo> getCurrentTunedInfos(); method @NonNull @RequiresPermission("android.permission.DVB_DEVICE") public java.util.List<android.media.tv.DvbDeviceInfo> getDvbDeviceList(); method @RequiresPermission(android.Manifest.permission.TV_INPUT_HARDWARE) public java.util.List<android.media.tv.TvInputHardwareInfo> getHardwareList(); method @RequiresPermission(android.Manifest.permission.READ_CONTENT_RATING_SYSTEMS) public java.util.List<android.media.tv.TvContentRatingSystemInfo> getTvContentRatingSystemList(); @@ -4909,6 +4929,10 @@ package android.media.tv { method public abstract void onStreamConfigChanged(android.media.tv.TvStreamConfig[]); } + public abstract static class TvInputManager.TvInputCallback { + method public void onCurrentTunedInfosUpdated(@NonNull java.util.List<android.media.tv.TunedInfo>); + } + public abstract class TvInputService extends android.app.Service { method @Nullable public android.media.tv.TvInputInfo onHardwareAdded(android.media.tv.TvInputHardwareInfo); method @Nullable public String onHardwareRemoved(android.media.tv.TvInputHardwareInfo); @@ -5036,6 +5060,7 @@ package android.media.tv.tuner { method @Nullable public android.media.tv.tuner.DemuxCapabilities getDemuxCapabilities(); method @Nullable public android.media.tv.tuner.frontend.FrontendInfo getFrontendInfo(); method @Nullable public android.media.tv.tuner.frontend.FrontendStatus getFrontendStatus(@NonNull int[]); + method public int linkFrontendToCiCam(int); method @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_TV_DESCRAMBLER) public android.media.tv.tuner.Descrambler openDescrambler(); method @Nullable public android.media.tv.tuner.dvr.DvrPlayback openDvrPlayback(long, @NonNull java.util.concurrent.Executor, @NonNull android.media.tv.tuner.dvr.OnPlaybackStatusChangedListener); method @Nullable public android.media.tv.tuner.dvr.DvrRecorder openDvrRecorder(long, @NonNull java.util.concurrent.Executor, @NonNull android.media.tv.tuner.dvr.OnRecordStatusChangedListener); @@ -5049,10 +5074,13 @@ package android.media.tv.tuner { method public void setResourceLostListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.tv.tuner.Tuner.OnResourceLostListener); method public void shareFrontendFromTuner(@NonNull android.media.tv.tuner.Tuner); method public int tune(@NonNull android.media.tv.tuner.frontend.FrontendSettings); + method public int unlinkFrontendToCiCam(int); method public void updateResourcePriority(int, int); field public static final int INVALID_AV_SYNC_ID = -1; // 0xffffffff field public static final int INVALID_FILTER_ID = -1; // 0xffffffff field public static final long INVALID_FILTER_ID_64BIT = -1L; // 0xffffffffffffffffL + field public static final int INVALID_FRONTEND_SETTING_FREQUENCY = -1; // 0xffffffff + field public static final int INVALID_LTS_ID = -1; // 0xffffffff field public static final int INVALID_MMTP_RECORD_EVENT_MPT_SEQUENCE_NUM = -1; // 0xffffffff field public static final int INVALID_STREAM_ID = 65535; // 0xffff field public static final long INVALID_TIMESTAMP = -1L; // 0xffffffffffffffffL @@ -5085,10 +5113,10 @@ package android.media.tv.tuner { package android.media.tv.tuner.dvr { public class DvrPlayback implements java.lang.AutoCloseable { - method public int attachFilter(@NonNull android.media.tv.tuner.filter.Filter); + method @Deprecated public int attachFilter(@NonNull android.media.tv.tuner.filter.Filter); method public void close(); method public int configure(@NonNull android.media.tv.tuner.dvr.DvrSettings); - method public int detachFilter(@NonNull android.media.tv.tuner.filter.Filter); + method @Deprecated public int detachFilter(@NonNull android.media.tv.tuner.filter.Filter); method public int flush(); method public long read(long); method public long read(@NonNull byte[], long, long); @@ -5182,12 +5210,45 @@ package android.media.tv.tuner.filter { public class AvSettings extends android.media.tv.tuner.filter.Settings { method @NonNull public static android.media.tv.tuner.filter.AvSettings.Builder builder(int, boolean); + method public int getAudioStreamType(); + method public int getVideoStreamType(); method public boolean isPassthrough(); + field public static final int AUDIO_STREAM_TYPE_AAC = 6; // 0x6 + field public static final int AUDIO_STREAM_TYPE_AC3 = 7; // 0x7 + field public static final int AUDIO_STREAM_TYPE_AC4 = 9; // 0x9 + field public static final int AUDIO_STREAM_TYPE_DRA = 15; // 0xf + field public static final int AUDIO_STREAM_TYPE_DTS = 10; // 0xa + field public static final int AUDIO_STREAM_TYPE_DTS_HD = 11; // 0xb + field public static final int AUDIO_STREAM_TYPE_EAC3 = 8; // 0x8 + field public static final int AUDIO_STREAM_TYPE_MP3 = 2; // 0x2 + field public static final int AUDIO_STREAM_TYPE_MPEG1 = 3; // 0x3 + field public static final int AUDIO_STREAM_TYPE_MPEG2 = 4; // 0x4 + field public static final int AUDIO_STREAM_TYPE_MPEGH = 5; // 0x5 + field public static final int AUDIO_STREAM_TYPE_OPUS = 13; // 0xd + field public static final int AUDIO_STREAM_TYPE_PCM = 1; // 0x1 + field public static final int AUDIO_STREAM_TYPE_UNDEFINED = 0; // 0x0 + field public static final int AUDIO_STREAM_TYPE_VORBIS = 14; // 0xe + field public static final int AUDIO_STREAM_TYPE_WMA = 12; // 0xc + field public static final int VIDEO_STREAM_TYPE_AV1 = 10; // 0xa + field public static final int VIDEO_STREAM_TYPE_AVC = 5; // 0x5 + field public static final int VIDEO_STREAM_TYPE_AVS = 11; // 0xb + field public static final int VIDEO_STREAM_TYPE_AVS2 = 12; // 0xc + field public static final int VIDEO_STREAM_TYPE_HEVC = 6; // 0x6 + field public static final int VIDEO_STREAM_TYPE_MPEG1 = 2; // 0x2 + field public static final int VIDEO_STREAM_TYPE_MPEG2 = 3; // 0x3 + field public static final int VIDEO_STREAM_TYPE_MPEG4P2 = 4; // 0x4 + field public static final int VIDEO_STREAM_TYPE_RESERVED = 1; // 0x1 + field public static final int VIDEO_STREAM_TYPE_UNDEFINED = 0; // 0x0 + field public static final int VIDEO_STREAM_TYPE_VC1 = 7; // 0x7 + field public static final int VIDEO_STREAM_TYPE_VP8 = 8; // 0x8 + field public static final int VIDEO_STREAM_TYPE_VP9 = 9; // 0x9 } public static class AvSettings.Builder { method @NonNull public android.media.tv.tuner.filter.AvSettings build(); + method @NonNull public android.media.tv.tuner.filter.AvSettings.Builder setAudioStreamType(int); method @NonNull public android.media.tv.tuner.filter.AvSettings.Builder setPassthrough(boolean); + method @NonNull public android.media.tv.tuner.filter.AvSettings.Builder setVideoStreamType(int); } public class DownloadEvent extends android.media.tv.tuner.filter.FilterEvent { @@ -5265,16 +5326,19 @@ package android.media.tv.tuner.filter { method @NonNull public static android.media.tv.tuner.filter.IpFilterConfiguration.Builder builder(); method @NonNull @Size(min=4, max=16) public byte[] getDstIpAddress(); method public int getDstPort(); + method @IntRange(from=0, to=61439) public int getIpFilterContextId(); method @NonNull @Size(min=4, max=16) public byte[] getSrcIpAddress(); method public int getSrcPort(); method public int getType(); method public boolean isPassthrough(); + field public static final int INVALID_IP_FILTER_CONTEXT_ID = -1; // 0xffffffff } public static final class IpFilterConfiguration.Builder { method @NonNull public android.media.tv.tuner.filter.IpFilterConfiguration build(); method @NonNull public android.media.tv.tuner.filter.IpFilterConfiguration.Builder setDstIpAddress(@NonNull byte[]); method @NonNull public android.media.tv.tuner.filter.IpFilterConfiguration.Builder setDstPort(int); + method @NonNull public android.media.tv.tuner.filter.IpFilterConfiguration.Builder setIpFilterContextId(int); method @NonNull public android.media.tv.tuner.filter.IpFilterConfiguration.Builder setPassthrough(boolean); method @NonNull public android.media.tv.tuner.filter.IpFilterConfiguration.Builder setSettings(@Nullable android.media.tv.tuner.filter.Settings); method @NonNull public android.media.tv.tuner.filter.IpFilterConfiguration.Builder setSrcIpAddress(@NonNull byte[]); @@ -5298,6 +5362,7 @@ package android.media.tv.tuner.filter { method public boolean isPrivateData(); method public boolean isPtsPresent(); method public boolean isSecureMemory(); + method public void release(); } public final class MmtpFilterConfiguration extends android.media.tv.tuner.filter.FilterConfiguration { @@ -5494,9 +5559,13 @@ package android.media.tv.tuner.frontend { public class AnalogFrontendSettings extends android.media.tv.tuner.frontend.FrontendSettings { method @NonNull public static android.media.tv.tuner.frontend.AnalogFrontendSettings.Builder builder(); + method public int getAftFlag(); method public int getSifStandard(); method public int getSignalType(); method public int getType(); + field public static final int AFT_FLAG_FALSE = 2; // 0x2 + field public static final int AFT_FLAG_TRUE = 1; // 0x1 + field public static final int AFT_FLAG_UNDEFINED = 0; // 0x0 field public static final int SIF_AUTO = 1; // 0x1 field public static final int SIF_BG = 2; // 0x2 field public static final int SIF_BG_A2 = 4; // 0x4 @@ -5529,6 +5598,7 @@ package android.media.tv.tuner.frontend { public static class AnalogFrontendSettings.Builder { method @NonNull public android.media.tv.tuner.frontend.AnalogFrontendSettings build(); + method @NonNull public android.media.tv.tuner.frontend.AnalogFrontendSettings.Builder setAftFlag(int); method @IntRange(from=1) @NonNull public android.media.tv.tuner.frontend.AnalogFrontendSettings.Builder setFrequency(int); method @NonNull public android.media.tv.tuner.frontend.AnalogFrontendSettings.Builder setSifStandard(int); method @NonNull public android.media.tv.tuner.frontend.AnalogFrontendSettings.Builder setSignalType(int); @@ -5644,6 +5714,69 @@ package android.media.tv.tuner.frontend { method @NonNull public android.media.tv.tuner.frontend.AtscFrontendSettings.Builder setModulation(int); } + public class DtmbFrontendCapabilities extends android.media.tv.tuner.frontend.FrontendCapabilities { + method public int getBandwidthCapability(); + method public int getCodeRateCapability(); + method public int getGuardIntervalCapability(); + method public int getModulationCapability(); + method public int getTimeInterleaveModeCapability(); + method public int getTransmissionModeCapability(); + } + + public class DtmbFrontendSettings extends android.media.tv.tuner.frontend.FrontendSettings { + method @NonNull public static android.media.tv.tuner.frontend.DtmbFrontendSettings.Builder builder(); + method public int getBandwidth(); + method public int getCodeRate(); + method public int getGuardInterval(); + method public int getModulation(); + method public int getTimeInterleaveMode(); + method public int getTransmissionMode(); + method public int getType(); + field public static final int BANDWIDTH_6MHZ = 4; // 0x4 + field public static final int BANDWIDTH_8MHZ = 2; // 0x2 + field public static final int BANDWIDTH_AUTO = 1; // 0x1 + field public static final int BANDWIDTH_UNDEFINED = 0; // 0x0 + field public static final int CODERATE_2_5 = 2; // 0x2 + field public static final int CODERATE_3_5 = 4; // 0x4 + field public static final int CODERATE_4_5 = 8; // 0x8 + field public static final int CODERATE_AUTO = 1; // 0x1 + field public static final int CODERATE_UNDEFINED = 0; // 0x0 + field public static final int GUARD_INTERVAL_AUTO = 1; // 0x1 + field public static final int GUARD_INTERVAL_PN_420_CONST = 16; // 0x10 + field public static final int GUARD_INTERVAL_PN_420_VARIOUS = 2; // 0x2 + field public static final int GUARD_INTERVAL_PN_595_CONST = 4; // 0x4 + field public static final int GUARD_INTERVAL_PN_945_CONST = 32; // 0x20 + field public static final int GUARD_INTERVAL_PN_945_VARIOUS = 8; // 0x8 + field public static final int GUARD_INTERVAL_PN_RESERVED = 64; // 0x40 + field public static final int GUARD_INTERVAL_UNDEFINED = 0; // 0x0 + field public static final int MODULATION_CONSTELLATION_16QAM = 8; // 0x8 + field public static final int MODULATION_CONSTELLATION_32QAM = 16; // 0x10 + field public static final int MODULATION_CONSTELLATION_4QAM = 2; // 0x2 + field public static final int MODULATION_CONSTELLATION_4QAM_NR = 4; // 0x4 + field public static final int MODULATION_CONSTELLATION_64QAM = 32; // 0x20 + field public static final int MODULATION_CONSTELLATION_AUTO = 1; // 0x1 + field public static final int MODULATION_CONSTELLATION_UNDEFINED = 0; // 0x0 + field public static final int TIME_INTERLEAVE_MODE_AUTO = 1; // 0x1 + field public static final int TIME_INTERLEAVE_MODE_TIMER_INT_240 = 2; // 0x2 + field public static final int TIME_INTERLEAVE_MODE_TIMER_INT_720 = 4; // 0x4 + field public static final int TIME_INTERLEAVE_MODE_UNDEFINED = 0; // 0x0 + field public static final int TRANSMISSION_MODE_AUTO = 1; // 0x1 + field public static final int TRANSMISSION_MODE_C1 = 2; // 0x2 + field public static final int TRANSMISSION_MODE_C3780 = 4; // 0x4 + field public static final int TRANSMISSION_MODE_UNDEFINED = 0; // 0x0 + } + + public static final class DtmbFrontendSettings.Builder { + method @NonNull public android.media.tv.tuner.frontend.DtmbFrontendSettings build(); + method @NonNull public android.media.tv.tuner.frontend.DtmbFrontendSettings.Builder setBandwidth(int); + method @NonNull public android.media.tv.tuner.frontend.DtmbFrontendSettings.Builder setCodeRate(int); + method @IntRange(from=1) @NonNull public android.media.tv.tuner.frontend.DtmbFrontendSettings.Builder setFrequency(int); + method @NonNull public android.media.tv.tuner.frontend.DtmbFrontendSettings.Builder setGuardInterval(int); + method @NonNull public android.media.tv.tuner.frontend.DtmbFrontendSettings.Builder setModulation(int); + method @NonNull public android.media.tv.tuner.frontend.DtmbFrontendSettings.Builder setTimeInterleaveMode(int); + method @NonNull public android.media.tv.tuner.frontend.DtmbFrontendSettings.Builder setTransmissionMode(int); + } + public class DvbcFrontendCapabilities extends android.media.tv.tuner.frontend.FrontendCapabilities { method public int getAnnexCapability(); method public int getFecCapability(); @@ -5658,6 +5791,7 @@ package android.media.tv.tuner.frontend { method public int getOuterFec(); method public int getSpectralInversion(); method public int getSymbolRate(); + method public int getTimeInterleaveMode(); method public int getType(); field public static final int ANNEX_A = 1; // 0x1 field public static final int ANNEX_B = 2; // 0x2 @@ -5673,9 +5807,20 @@ package android.media.tv.tuner.frontend { field public static final int OUTER_FEC_OUTER_FEC_NONE = 1; // 0x1 field public static final int OUTER_FEC_OUTER_FEC_RS = 2; // 0x2 field public static final int OUTER_FEC_UNDEFINED = 0; // 0x0 - field public static final int SPECTRAL_INVERSION_INVERTED = 2; // 0x2 - field public static final int SPECTRAL_INVERSION_NORMAL = 1; // 0x1 - field public static final int SPECTRAL_INVERSION_UNDEFINED = 0; // 0x0 + field @Deprecated public static final int SPECTRAL_INVERSION_INVERTED = 2; // 0x2 + field @Deprecated public static final int SPECTRAL_INVERSION_NORMAL = 1; // 0x1 + field @Deprecated public static final int SPECTRAL_INVERSION_UNDEFINED = 0; // 0x0 + field public static final int TIME_INTERLEAVE_MODE_128_1_0 = 2; // 0x2 + field public static final int TIME_INTERLEAVE_MODE_128_1_1 = 4; // 0x4 + field public static final int TIME_INTERLEAVE_MODE_128_2 = 128; // 0x80 + field public static final int TIME_INTERLEAVE_MODE_128_3 = 256; // 0x100 + field public static final int TIME_INTERLEAVE_MODE_128_4 = 512; // 0x200 + field public static final int TIME_INTERLEAVE_MODE_16_8 = 32; // 0x20 + field public static final int TIME_INTERLEAVE_MODE_32_4 = 16; // 0x10 + field public static final int TIME_INTERLEAVE_MODE_64_2 = 8; // 0x8 + field public static final int TIME_INTERLEAVE_MODE_8_16 = 64; // 0x40 + field public static final int TIME_INTERLEAVE_MODE_AUTO = 1; // 0x1 + field public static final int TIME_INTERLEAVE_MODE_UNDEFINED = 0; // 0x0 } public static class DvbcFrontendSettings.Builder { @@ -5687,6 +5832,7 @@ package android.media.tv.tuner.frontend { method @NonNull public android.media.tv.tuner.frontend.DvbcFrontendSettings.Builder setOuterFec(int); method @NonNull public android.media.tv.tuner.frontend.DvbcFrontendSettings.Builder setSpectralInversion(int); method @NonNull public android.media.tv.tuner.frontend.DvbcFrontendSettings.Builder setSymbolRate(int); + method @NonNull public android.media.tv.tuner.frontend.DvbcFrontendSettings.Builder setTimeInterleaveMode(int); } public class DvbsCodeRate { @@ -5718,10 +5864,12 @@ package android.media.tv.tuner.frontend { method public int getModulation(); method public int getPilot(); method public int getRolloff(); + method public int getScanType(); method public int getStandard(); method public int getSymbolRate(); method public int getType(); method public int getVcmMode(); + method public boolean isDiseqcRxMessage(); field public static final int MODULATION_AUTO = 1; // 0x1 field public static final int MODULATION_MOD_128APSK = 2048; // 0x800 field public static final int MODULATION_MOD_16APSK = 256; // 0x100 @@ -5748,6 +5896,11 @@ package android.media.tv.tuner.frontend { field public static final int ROLLOFF_0_35 = 1; // 0x1 field public static final int ROLLOFF_0_5 = 6; // 0x6 field public static final int ROLLOFF_UNDEFINED = 0; // 0x0 + field public static final int SCAN_TYPE_DIRECT = 1; // 0x1 + field public static final int SCAN_TYPE_DISEQC = 2; // 0x2 + field public static final int SCAN_TYPE_JESS = 4; // 0x4 + field public static final int SCAN_TYPE_UNDEFINED = 0; // 0x0 + field public static final int SCAN_TYPE_UNICABLE = 3; // 0x3 field public static final int STANDARD_AUTO = 1; // 0x1 field public static final int STANDARD_S = 2; // 0x2 field public static final int STANDARD_S2 = 4; // 0x4 @@ -5760,11 +5913,13 @@ package android.media.tv.tuner.frontend { public static class DvbsFrontendSettings.Builder { method @NonNull public android.media.tv.tuner.frontend.DvbsFrontendSettings build(); method @NonNull public android.media.tv.tuner.frontend.DvbsFrontendSettings.Builder setCodeRate(@Nullable android.media.tv.tuner.frontend.DvbsCodeRate); + method @NonNull public android.media.tv.tuner.frontend.DvbsFrontendSettings.Builder setDiseqcRxMessage(boolean); method @IntRange(from=1) @NonNull public android.media.tv.tuner.frontend.DvbsFrontendSettings.Builder setFrequency(int); method @NonNull public android.media.tv.tuner.frontend.DvbsFrontendSettings.Builder setInputStreamId(int); method @NonNull public android.media.tv.tuner.frontend.DvbsFrontendSettings.Builder setModulation(int); method @NonNull public android.media.tv.tuner.frontend.DvbsFrontendSettings.Builder setPilot(int); method @NonNull public android.media.tv.tuner.frontend.DvbsFrontendSettings.Builder setRolloff(int); + method @NonNull public android.media.tv.tuner.frontend.DvbsFrontendSettings.Builder setScanType(int); method @NonNull public android.media.tv.tuner.frontend.DvbsFrontendSettings.Builder setStandard(int); method @NonNull public android.media.tv.tuner.frontend.DvbsFrontendSettings.Builder setSymbolRate(int); method @NonNull public android.media.tv.tuner.frontend.DvbsFrontendSettings.Builder setVcmMode(int); @@ -5817,10 +5972,14 @@ package android.media.tv.tuner.frontend { field public static final int CODERATE_AUTO = 1; // 0x1 field public static final int CODERATE_UNDEFINED = 0; // 0x0 field public static final int CONSTELLATION_16QAM = 4; // 0x4 + field public static final int CONSTELLATION_16QAM_R = 64; // 0x40 field public static final int CONSTELLATION_256QAM = 16; // 0x10 + field public static final int CONSTELLATION_256QAM_R = 256; // 0x100 field public static final int CONSTELLATION_64QAM = 8; // 0x8 + field public static final int CONSTELLATION_64QAM_R = 128; // 0x80 field public static final int CONSTELLATION_AUTO = 1; // 0x1 field public static final int CONSTELLATION_QPSK = 2; // 0x2 + field public static final int CONSTELLATION_QPSK_R = 32; // 0x20 field public static final int CONSTELLATION_UNDEFINED = 0; // 0x0 field public static final int GUARD_INTERVAL_19_128 = 64; // 0x40 field public static final int GUARD_INTERVAL_19_256 = 128; // 0x80 @@ -5854,6 +6013,9 @@ package android.media.tv.tuner.frontend { field public static final int TRANSMISSION_MODE_4K = 8; // 0x8 field public static final int TRANSMISSION_MODE_8K = 4; // 0x4 field public static final int TRANSMISSION_MODE_AUTO = 1; // 0x1 + field public static final int TRANSMISSION_MODE_EXTENDED_16K = 256; // 0x100 + field public static final int TRANSMISSION_MODE_EXTENDED_32K = 512; // 0x200 + field public static final int TRANSMISSION_MODE_EXTENDED_8K = 128; // 0x80 field public static final int TRANSMISSION_MODE_UNDEFINED = 0; // 0x0 } @@ -5891,8 +6053,12 @@ package android.media.tv.tuner.frontend { } public abstract class FrontendSettings { + method public int getEndFrequency(); method public int getFrequency(); + method public int getFrontendSpectralInversion(); method public abstract int getType(); + method @IntRange(from=1) public void setEndFrequency(int); + method public void setSpectralInversion(int); field public static final long FEC_11_15 = 4194304L; // 0x400000L field public static final long FEC_11_20 = 8388608L; // 0x800000L field public static final long FEC_11_45 = 16777216L; // 0x1000000L @@ -5930,9 +6096,13 @@ package android.media.tv.tuner.frontend { field public static final long FEC_9_20 = 2097152L; // 0x200000L field public static final long FEC_AUTO = 1L; // 0x1L field public static final long FEC_UNDEFINED = 0L; // 0x0L + field public static final int FRONTEND_SPECTRAL_INVERSION_INVERTED = 2; // 0x2 + field public static final int FRONTEND_SPECTRAL_INVERSION_NORMAL = 1; // 0x1 + field public static final int FRONTEND_SPECTRAL_INVERSION_UNDEFINED = 0; // 0x0 field public static final int TYPE_ANALOG = 1; // 0x1 field public static final int TYPE_ATSC = 2; // 0x2 field public static final int TYPE_ATSC3 = 3; // 0x3 + field public static final int TYPE_DTMB = 10; // 0xa field public static final int TYPE_DVBC = 4; // 0x4 field public static final int TYPE_DVBS = 5; // 0x5 field public static final int TYPE_DVBT = 6; // 0x6 @@ -5945,14 +6115,21 @@ package android.media.tv.tuner.frontend { public class FrontendStatus { method public int getAgc(); method @NonNull public android.media.tv.tuner.frontend.FrontendStatus.Atsc3PlpTuningInfo[] getAtsc3PlpTuningInfo(); + method public int getBandwidth(); method public int getBer(); + method @NonNull public int[] getBers(); + method @NonNull public int[] getCodeRates(); method public int getFreqOffset(); + method public int getGuardInterval(); method public int getHierarchy(); method public long getInnerFec(); + method @NonNull public int[] getInterleaving(); + method @NonNull public int[] getIsdbtSegment(); method @NonNull public boolean[] getLayerErrors(); method public int getLnbVoltage(); method public int getMer(); method public int getModulation(); + method @NonNull public int[] getModulationsExt(); method public int getPer(); method public int getPerBer(); method public int getPlpId(); @@ -5961,23 +6138,34 @@ package android.media.tv.tuner.frontend { method public int getSnr(); method public int getSpectralInversion(); method public int getSymbolRate(); + method public int getSystemId(); + method public int getTransmissionMode(); + method @NonNull public int[] getTsDataRate(); + method public int getUec(); method public boolean isDemodLocked(); method public boolean isEwbs(); method public boolean isLnaOn(); method public boolean isRfLocked(); field public static final int FRONTEND_STATUS_TYPE_AGC = 14; // 0xe field public static final int FRONTEND_STATUS_TYPE_ATSC3_PLP_INFO = 21; // 0x15 + field public static final int FRONTEND_STATUS_TYPE_BANDWIDTH = 25; // 0x19 field public static final int FRONTEND_STATUS_TYPE_BER = 2; // 0x2 + field public static final int FRONTEND_STATUS_TYPE_BERS = 23; // 0x17 + field public static final int FRONTEND_STATUS_TYPE_CODERATES = 24; // 0x18 field public static final int FRONTEND_STATUS_TYPE_DEMOD_LOCK = 0; // 0x0 field public static final int FRONTEND_STATUS_TYPE_EWBS = 13; // 0xd field public static final int FRONTEND_STATUS_TYPE_FEC = 8; // 0x8 field public static final int FRONTEND_STATUS_TYPE_FREQ_OFFSET = 18; // 0x12 + field public static final int FRONTEND_STATUS_TYPE_GUARD_INTERVAL = 26; // 0x1a field public static final int FRONTEND_STATUS_TYPE_HIERARCHY = 19; // 0x13 + field public static final int FRONTEND_STATUS_TYPE_INTERLEAVINGS = 30; // 0x1e + field public static final int FRONTEND_STATUS_TYPE_ISDBT_SEGMENTS = 31; // 0x1f field public static final int FRONTEND_STATUS_TYPE_LAYER_ERROR = 16; // 0x10 field public static final int FRONTEND_STATUS_TYPE_LNA = 15; // 0xf field public static final int FRONTEND_STATUS_TYPE_LNB_VOLTAGE = 11; // 0xb field public static final int FRONTEND_STATUS_TYPE_MER = 17; // 0x11 field public static final int FRONTEND_STATUS_TYPE_MODULATION = 9; // 0x9 + field public static final int FRONTEND_STATUS_TYPE_MODULATIONS_EXT = 22; // 0x16 field public static final int FRONTEND_STATUS_TYPE_PER = 3; // 0x3 field public static final int FRONTEND_STATUS_TYPE_PLP_ID = 12; // 0xc field public static final int FRONTEND_STATUS_TYPE_PRE_BER = 4; // 0x4 @@ -5987,6 +6175,10 @@ package android.media.tv.tuner.frontend { field public static final int FRONTEND_STATUS_TYPE_SNR = 1; // 0x1 field public static final int FRONTEND_STATUS_TYPE_SPECTRAL = 10; // 0xa field public static final int FRONTEND_STATUS_TYPE_SYMBOL_RATE = 7; // 0x7 + field public static final int FRONTEND_STATUS_TYPE_T2_SYSTEM_ID = 29; // 0x1d + field public static final int FRONTEND_STATUS_TYPE_TRANSMISSION_MODE = 27; // 0x1b + field public static final int FRONTEND_STATUS_TYPE_TS_DATA_RATES = 32; // 0x20 + field public static final int FRONTEND_STATUS_TYPE_UEC = 28; // 0x1c } public static class FrontendStatus.Atsc3PlpTuningInfo { @@ -6150,7 +6342,9 @@ package android.media.tv.tuner.frontend { method public void onHierarchyReported(int); method public void onInputStreamIdsReported(@NonNull int[]); method public void onLocked(); + method public default void onModulationReported(int); method public void onPlpIdsReported(@NonNull int[]); + method public default void onPriorityReported(boolean); method public void onProgress(@IntRange(from=0, to=100) int); method public void onScanStopped(); method public void onSignalTypeReported(int); @@ -7879,6 +8073,10 @@ package android.net.wifi.aware { method @Deprecated public android.net.NetworkSpecifier createNetworkSpecifierPmk(@NonNull android.net.wifi.aware.PeerHandle, @NonNull byte[]); } + public class WifiAwareManager { + method public void enableInstantCommunicationMode(boolean); + } + public class WifiAwareSession implements java.lang.AutoCloseable { method public android.net.NetworkSpecifier createNetworkSpecifierPmk(int, @NonNull byte[], @NonNull byte[]); } @@ -8612,6 +8810,22 @@ package android.os { method public boolean hasSingleFileDescriptor(); } + public interface Parcelable { + field public static final int PARCELABLE_STABILITY_LOCAL = 0; // 0x0 + field public static final int PARCELABLE_STABILITY_VINTF = 1; // 0x1 + } + + public final class ParcelableHolder implements android.os.Parcelable { + ctor public ParcelableHolder(int); + method public int describeContents(); + method @Nullable public <T extends android.os.Parcelable> T getParcelable(@NonNull Class<T>); + method public int getStability(); + method public void readFromParcel(@NonNull android.os.Parcel); + method public boolean setParcelable(@Nullable android.os.Parcelable); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.os.ParcelableHolder> CREATOR; + } + public final class PowerManager { method @RequiresPermission(allOf={android.Manifest.permission.READ_DREAM_STATE, android.Manifest.permission.WRITE_DREAM_STATE}) public void dream(long); method @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public boolean forceSuspend(); @@ -11040,6 +11254,25 @@ package android.telephony { field public static final String MBMS_STREAMING_SERVICE_ACTION = "android.telephony.action.EmbmsStreaming"; } + public final class ModemActivityInfo implements android.os.Parcelable { + method public int describeContents(); + method @NonNull public android.telephony.ModemActivityInfo getDelta(@NonNull android.telephony.ModemActivityInfo); + method public long getIdleTimeMillis(); + method public static int getNumTxPowerLevels(); + method public long getReceiveTimeMillis(); + method public long getSleepTimeMillis(); + method public long getTimestampMillis(); + method public long getTransmitDurationMillisAtPowerLevel(int); + method @NonNull public android.util.Range<java.lang.Integer> getTransmitPowerRange(int); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ModemActivityInfo> CREATOR; + field public static final int TX_POWER_LEVEL_0 = 0; // 0x0 + field public static final int TX_POWER_LEVEL_1 = 1; // 0x1 + field public static final int TX_POWER_LEVEL_2 = 2; // 0x2 + field public static final int TX_POWER_LEVEL_3 = 3; // 0x3 + field public static final int TX_POWER_LEVEL_4 = 4; // 0x4 + } + public final class NetworkRegistrationInfo implements android.os.Parcelable { method @Nullable public android.telephony.DataSpecificRegistrationInfo getDataSpecificInfo(); method public int getRegistrationState(); @@ -11292,6 +11525,10 @@ package android.telephony { field public static final int ROAMING_TYPE_UNKNOWN = 1; // 0x1 } + public class SignalStrength implements android.os.Parcelable { + ctor public SignalStrength(@NonNull android.telephony.SignalStrength); + } + public final class SmsCbCmasInfo implements android.os.Parcelable { ctor public SmsCbCmasInfo(int, int, int, int, int, int); method public int describeContents(); @@ -13092,7 +13329,7 @@ package android.webkit { method @Nullable public String findProxyForUrl(@NonNull String); method @NonNull public static android.webkit.PacProcessor getInstance(); method @Nullable public default android.net.Network getNetwork(); - method public default void releasePacProcessor(); + method public default void release(); method public default void setNetwork(@Nullable android.net.Network); method public boolean setProxyScript(@NonNull String); } diff --git a/api/system-lint-baseline.txt b/api/system-lint-baseline.txt index 7195144f28e5..773ecd034a8e 100644 --- a/api/system-lint-baseline.txt +++ b/api/system-lint-baseline.txt @@ -159,8 +159,6 @@ MissingNullability: android.telephony.ModemActivityInfo#toString(): MissingNullability: android.telephony.ModemActivityInfo#writeToParcel(android.os.Parcel, int) parameter #0: -MissingNullability: android.telephony.ModemActivityInfo.TransmitPower#toString(): - MissingNullability: android.telephony.NetworkService#onUnbind(android.content.Intent) parameter #0: MissingNullability: android.telephony.SmsCbCmasInfo#toString(): diff --git a/api/test-current.txt b/api/test-current.txt index 9069eea8d93c..011e86f6cd4f 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -14,6 +14,7 @@ package android { field public static final String FORCE_STOP_PACKAGES = "android.permission.FORCE_STOP_PACKAGES"; field public static final String MANAGE_ACTIVITY_STACKS = "android.permission.MANAGE_ACTIVITY_STACKS"; field public static final String MANAGE_CRATES = "android.permission.MANAGE_CRATES"; + field public static final String MANAGE_NOTIFICATION_LISTENERS = "android.permission.MANAGE_NOTIFICATION_LISTENERS"; field public static final String MANAGE_ROLLBACKS = "android.permission.MANAGE_ROLLBACKS"; field public static final String NETWORK_SETTINGS = "android.permission.NETWORK_SETTINGS"; field public static final String NETWORK_STACK = "android.permission.NETWORK_STACK"; @@ -79,20 +80,15 @@ package android.app { } public class ActivityManager { - method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public void addOnUidImportanceListener(android.app.ActivityManager.OnUidImportanceListener, int); + method @RequiresPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER) public void addHomeVisibilityListener(@NonNull java.util.concurrent.Executor, @NonNull android.app.HomeVisibilityListener); method public void alwaysShowUnsupportedCompileSdkWarning(android.content.ComponentName); - method @RequiresPermission(android.Manifest.permission.FORCE_STOP_PACKAGES) public void forceStopPackage(String); - method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public int getPackageImportance(String); method public long getTotalRam(); - method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public int getUidImportance(int); method @RequiresPermission(android.Manifest.permission.INJECT_EVENTS) public void holdLock(int); method public static boolean isHighEndGfx(); - method @RequiresPermission(android.Manifest.permission.FORCE_STOP_PACKAGES) public void killProcessesWhenImperceptible(@NonNull int[], @NonNull String); - method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public void removeOnUidImportanceListener(android.app.ActivityManager.OnUidImportanceListener); + method @RequiresPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER) public void removeHomeVisibilityListener(@NonNull android.app.HomeVisibilityListener); method @RequiresPermission(android.Manifest.permission.RESET_APP_ERRORS) public void resetAppErrors(); method public static void resumeAppSwitches() throws android.os.RemoteException; method @RequiresPermission(android.Manifest.permission.CHANGE_CONFIGURATION) public void scheduleApplicationInfoChanged(java.util.List<java.lang.String>, int); - method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean switchUser(@NonNull android.os.UserHandle); field public static final int PROCESS_CAPABILITY_ALL = 7; // 0x7 field public static final int PROCESS_CAPABILITY_ALL_EXPLICIT = 1; // 0x1 field public static final int PROCESS_CAPABILITY_ALL_IMPLICIT = 6; // 0x6 @@ -102,10 +98,6 @@ package android.app { field public static final int PROCESS_CAPABILITY_NONE = 0; // 0x0 } - public static interface ActivityManager.OnUidImportanceListener { - method public void onUidImportance(int, int); - } - public static class ActivityManager.RunningAppProcessInfo implements android.os.Parcelable { field public static final int IMPORTANCE_CANT_SAVE_STATE_PRE_26 = 170; // 0xaa } @@ -201,15 +193,10 @@ package android.app { public class AppOpsManager { method @RequiresPermission("android.permission.MANAGE_APPOPS") public void addHistoricalOps(@NonNull android.app.AppOpsManager.HistoricalOps); method @RequiresPermission("android.permission.MANAGE_APPOPS") public void clearHistory(); - method @Nullable @RequiresPermission(android.Manifest.permission.GET_APP_OPS_STATS) public android.app.RuntimeAppOpAccessMessage collectRuntimeAppOpAccessMessage(); - method @RequiresPermission(android.Manifest.permission.GET_APP_OPS_STATS) public void getHistoricalOps(@NonNull android.app.AppOpsManager.HistoricalOpsRequest, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.app.AppOpsManager.HistoricalOps>); method @RequiresPermission("android.permission.MANAGE_APPOPS") public void getHistoricalOpsFromDiskRaw(@NonNull android.app.AppOpsManager.HistoricalOpsRequest, @Nullable java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.app.AppOpsManager.HistoricalOps>); method public static int getNumOps(); - method public static String[] getOpStrs(); - method @NonNull @RequiresPermission(android.Manifest.permission.GET_APP_OPS_STATS) public java.util.List<android.app.AppOpsManager.PackageOps> getOpsForPackage(int, @NonNull String, @Nullable java.lang.String...); method public boolean isOperationActive(int, int, String); method @RequiresPermission("android.permission.MANAGE_APPOPS") public void offsetHistory(long); - method public static int opToDefaultMode(@NonNull String); method public static String opToPermission(int); method public static int permissionToOpCode(String); method @RequiresPermission("android.permission.MANAGE_APPOPS") public void rebootHistory(long); @@ -217,8 +204,6 @@ package android.app { method @RequiresPermission("android.permission.MANAGE_APPOPS") public void resetHistoryParameters(); method @RequiresPermission("android.permission.MANAGE_APPOPS") public void setHistoryParameters(int, long, int); method @RequiresPermission("android.permission.MANAGE_APP_OPS_MODES") public void setMode(int, int, String, int); - method @RequiresPermission("android.permission.MANAGE_APP_OPS_MODES") public void setMode(@NonNull String, int, @Nullable String, int); - method @RequiresPermission("android.permission.MANAGE_APP_OPS_MODES") public void setUidMode(@NonNull String, int, int); method public static int strOpToOp(@NonNull String); field public static final int HISTORICAL_MODE_DISABLED = 0; // 0x0 field public static final int HISTORICAL_MODE_ENABLED_ACTIVE = 1; // 0x1 @@ -226,225 +211,19 @@ package android.app { field public static final String KEY_BG_STATE_SETTLE_TIME = "bg_state_settle_time"; field public static final String KEY_FG_SERVICE_STATE_SETTLE_TIME = "fg_service_state_settle_time"; field public static final String KEY_TOP_STATE_SETTLE_TIME = "top_state_settle_time"; - field public static final String OPSTR_ACCEPT_HANDOVER = "android:accept_handover"; - field public static final String OPSTR_ACCESS_NOTIFICATIONS = "android:access_notifications"; - field public static final String OPSTR_ACTIVATE_VPN = "android:activate_vpn"; - field public static final String OPSTR_ASSIST_SCREENSHOT = "android:assist_screenshot"; - field public static final String OPSTR_ASSIST_STRUCTURE = "android:assist_structure"; - field public static final String OPSTR_AUDIO_ACCESSIBILITY_VOLUME = "android:audio_accessibility_volume"; - field public static final String OPSTR_AUDIO_ALARM_VOLUME = "android:audio_alarm_volume"; - field public static final String OPSTR_AUDIO_BLUETOOTH_VOLUME = "android:audio_bluetooth_volume"; - field public static final String OPSTR_AUDIO_MASTER_VOLUME = "android:audio_master_volume"; - field public static final String OPSTR_AUDIO_MEDIA_VOLUME = "android:audio_media_volume"; - field public static final String OPSTR_AUDIO_NOTIFICATION_VOLUME = "android:audio_notification_volume"; - field public static final String OPSTR_AUDIO_RING_VOLUME = "android:audio_ring_volume"; - field public static final String OPSTR_AUDIO_VOICE_VOLUME = "android:audio_voice_volume"; - field public static final String OPSTR_BIND_ACCESSIBILITY_SERVICE = "android:bind_accessibility_service"; - field public static final String OPSTR_CHANGE_WIFI_STATE = "android:change_wifi_state"; - field public static final String OPSTR_GET_ACCOUNTS = "android:get_accounts"; - field public static final String OPSTR_GPS = "android:gps"; - field public static final String OPSTR_INSTANT_APP_START_FOREGROUND = "android:instant_app_start_foreground"; - field public static final String OPSTR_LEGACY_STORAGE = "android:legacy_storage"; - field public static final String OPSTR_MANAGE_EXTERNAL_STORAGE = "android:manage_external_storage"; - field public static final String OPSTR_MANAGE_IPSEC_TUNNELS = "android:manage_ipsec_tunnels"; - field public static final String OPSTR_MUTE_MICROPHONE = "android:mute_microphone"; - field public static final String OPSTR_NEIGHBORING_CELLS = "android:neighboring_cells"; - field public static final String OPSTR_PLAY_AUDIO = "android:play_audio"; - field public static final String OPSTR_POST_NOTIFICATION = "android:post_notification"; - field public static final String OPSTR_PROJECT_MEDIA = "android:project_media"; - field public static final String OPSTR_READ_CLIPBOARD = "android:read_clipboard"; - field public static final String OPSTR_READ_ICC_SMS = "android:read_icc_sms"; - field public static final String OPSTR_READ_MEDIA_AUDIO = "android:read_media_audio"; - field public static final String OPSTR_READ_MEDIA_IMAGES = "android:read_media_images"; - field public static final String OPSTR_READ_MEDIA_VIDEO = "android:read_media_video"; - field public static final String OPSTR_RECEIVE_EMERGENCY_BROADCAST = "android:receive_emergency_broadcast"; - field public static final String OPSTR_REQUEST_DELETE_PACKAGES = "android:request_delete_packages"; - field public static final String OPSTR_REQUEST_INSTALL_PACKAGES = "android:request_install_packages"; - field public static final String OPSTR_RUN_ANY_IN_BACKGROUND = "android:run_any_in_background"; - field public static final String OPSTR_RUN_IN_BACKGROUND = "android:run_in_background"; - field public static final String OPSTR_START_FOREGROUND = "android:start_foreground"; - field public static final String OPSTR_TAKE_AUDIO_FOCUS = "android:take_audio_focus"; - field public static final String OPSTR_TAKE_MEDIA_BUTTONS = "android:take_media_buttons"; - field public static final String OPSTR_TOAST_WINDOW = "android:toast_window"; - field public static final String OPSTR_TURN_SCREEN_ON = "android:turn_screen_on"; - field public static final String OPSTR_VIBRATE = "android:vibrate"; - field public static final String OPSTR_WAKE_LOCK = "android:wake_lock"; - field public static final String OPSTR_WIFI_SCAN = "android:wifi_scan"; - field public static final String OPSTR_WRITE_CLIPBOARD = "android:write_clipboard"; - field public static final String OPSTR_WRITE_ICC_SMS = "android:write_icc_sms"; - field public static final String OPSTR_WRITE_MEDIA_AUDIO = "android:write_media_audio"; - field public static final String OPSTR_WRITE_MEDIA_IMAGES = "android:write_media_images"; - field public static final String OPSTR_WRITE_MEDIA_VIDEO = "android:write_media_video"; - field public static final String OPSTR_WRITE_SMS = "android:write_sms"; - field public static final String OPSTR_WRITE_WALLPAPER = "android:write_wallpaper"; field public static final int OP_COARSE_LOCATION = 0; // 0x0 - field public static final int OP_FLAGS_ALL = 31; // 0x1f - field public static final int OP_FLAG_SELF = 1; // 0x1 - field public static final int OP_FLAG_TRUSTED_PROXIED = 8; // 0x8 - field public static final int OP_FLAG_TRUSTED_PROXY = 2; // 0x2 - field public static final int OP_FLAG_UNTRUSTED_PROXIED = 16; // 0x10 - field public static final int OP_FLAG_UNTRUSTED_PROXY = 4; // 0x4 field public static final int OP_RECORD_AUDIO = 27; // 0x1b field public static final int OP_START_FOREGROUND = 76; // 0x4c field public static final int OP_SYSTEM_ALERT_WINDOW = 24; // 0x18 field public static final long SECURITY_EXCEPTION_ON_INVALID_ATTRIBUTION_TAG_CHANGE = 151105954L; // 0x901b1a2L - field public static final int UID_STATE_BACKGROUND = 600; // 0x258 - field public static final int UID_STATE_CACHED = 700; // 0x2bc - field public static final int UID_STATE_FOREGROUND = 500; // 0x1f4 - field public static final int UID_STATE_FOREGROUND_SERVICE = 400; // 0x190 - field @Deprecated public static final int UID_STATE_FOREGROUND_SERVICE_LOCATION = 300; // 0x12c - field public static final int UID_STATE_PERSISTENT = 100; // 0x64 - field public static final int UID_STATE_TOP = 200; // 0xc8 - } - - public static final class AppOpsManager.AttributedHistoricalOps implements android.os.Parcelable { - method public int describeContents(); - method @Nullable public android.app.AppOpsManager.HistoricalOp getOp(@NonNull String); - method @NonNull public android.app.AppOpsManager.HistoricalOp getOpAt(@IntRange(from=0) int); - method @IntRange(from=0) public int getOpCount(); - method @Nullable public String getTag(); - method public void writeToParcel(@NonNull android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.app.AppOpsManager.AttributedHistoricalOps> CREATOR; - } - - public static final class AppOpsManager.AttributedOpEntry implements android.os.Parcelable { - method public int describeContents(); - method public long getLastAccessBackgroundTime(int); - method public long getLastAccessForegroundTime(int); - method public long getLastAccessTime(int); - method public long getLastAccessTime(int, int, int); - method public long getLastBackgroundDuration(int); - method @Nullable public android.app.AppOpsManager.OpEventProxyInfo getLastBackgroundProxyInfo(int); - method public long getLastDuration(int); - method public long getLastDuration(int, int, int); - method public long getLastForegroundDuration(int); - method @Nullable public android.app.AppOpsManager.OpEventProxyInfo getLastForegroundProxyInfo(int); - method @Nullable public android.app.AppOpsManager.OpEventProxyInfo getLastProxyInfo(int); - method @Nullable public android.app.AppOpsManager.OpEventProxyInfo getLastProxyInfo(int, int, int); - method public long getLastRejectBackgroundTime(int); - method public long getLastRejectForegroundTime(int); - method public long getLastRejectTime(int); - method public long getLastRejectTime(int, int, int); - method public boolean isRunning(); - method public void writeToParcel(@NonNull android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.app.AppOpsManager.AttributedOpEntry> CREATOR; - } - - public static final class AppOpsManager.HistoricalOp implements android.os.Parcelable { - method public int describeContents(); - method public long getAccessCount(int, int, int); - method public long getAccessDuration(int, int, int); - method public long getBackgroundAccessCount(int); - method public long getBackgroundAccessDuration(int); - method public long getBackgroundRejectCount(int); - method public long getForegroundAccessCount(int); - method public long getForegroundAccessDuration(int); - method public long getForegroundRejectCount(int); - method @NonNull public String getOpName(); - method public long getRejectCount(int, int, int); - method public void writeToParcel(android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.app.AppOpsManager.HistoricalOp> CREATOR; } public static final class AppOpsManager.HistoricalOps implements android.os.Parcelable { ctor public AppOpsManager.HistoricalOps(long, long); - method public int describeContents(); - method public long getBeginTimeMillis(); - method public long getEndTimeMillis(); - method @IntRange(from=0) public int getUidCount(); - method @Nullable public android.app.AppOpsManager.HistoricalUidOps getUidOps(int); - method @NonNull public android.app.AppOpsManager.HistoricalUidOps getUidOpsAt(@IntRange(from=0) int); method public void increaseAccessCount(int, int, @NonNull String, @Nullable String, int, int, long); method public void increaseAccessDuration(int, int, @NonNull String, @Nullable String, int, int, long); method public void increaseRejectCount(int, int, @NonNull String, @Nullable String, int, int, long); method public void offsetBeginAndEndTime(long); - method public void writeToParcel(android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.app.AppOpsManager.HistoricalOps> CREATOR; - } - - public static final class AppOpsManager.HistoricalOpsRequest { - } - - public static final class AppOpsManager.HistoricalOpsRequest.Builder { - ctor public AppOpsManager.HistoricalOpsRequest.Builder(long, long); - method @NonNull public android.app.AppOpsManager.HistoricalOpsRequest build(); - method @NonNull public android.app.AppOpsManager.HistoricalOpsRequest.Builder setAttributionTag(@Nullable String); - method @NonNull public android.app.AppOpsManager.HistoricalOpsRequest.Builder setFlags(int); - method @NonNull public android.app.AppOpsManager.HistoricalOpsRequest.Builder setOpNames(@Nullable java.util.List<java.lang.String>); - method @NonNull public android.app.AppOpsManager.HistoricalOpsRequest.Builder setPackageName(@Nullable String); - method @NonNull public android.app.AppOpsManager.HistoricalOpsRequest.Builder setUid(int); - } - - public static final class AppOpsManager.HistoricalPackageOps implements android.os.Parcelable { - method public int describeContents(); - method @Nullable public android.app.AppOpsManager.AttributedHistoricalOps getAttributedOps(@NonNull String); - method @NonNull public android.app.AppOpsManager.AttributedHistoricalOps getAttributedOpsAt(@IntRange(from=0) int); - method @IntRange(from=0) public int getAttributedOpsCount(); - method @Nullable public android.app.AppOpsManager.HistoricalOp getOp(@NonNull String); - method @NonNull public android.app.AppOpsManager.HistoricalOp getOpAt(@IntRange(from=0) int); - method @IntRange(from=0) public int getOpCount(); - method @NonNull public String getPackageName(); - method public void writeToParcel(@NonNull android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.app.AppOpsManager.HistoricalPackageOps> CREATOR; - } - - public static final class AppOpsManager.HistoricalUidOps implements android.os.Parcelable { - method public int describeContents(); - method @IntRange(from=0) public int getPackageCount(); - method @Nullable public android.app.AppOpsManager.HistoricalPackageOps getPackageOps(@NonNull String); - method @NonNull public android.app.AppOpsManager.HistoricalPackageOps getPackageOpsAt(@IntRange(from=0) int); - method public int getUid(); - method public void writeToParcel(android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.app.AppOpsManager.HistoricalUidOps> CREATOR; - } - - public static final class AppOpsManager.OpEntry implements android.os.Parcelable { - method public int describeContents(); - method @NonNull public java.util.Map<java.lang.String,android.app.AppOpsManager.AttributedOpEntry> getAttributedOpEntries(); - method @Deprecated public long getDuration(); - method public long getLastAccessBackgroundTime(int); - method public long getLastAccessForegroundTime(int); - method public long getLastAccessTime(int); - method public long getLastAccessTime(int, int, int); - method public long getLastBackgroundDuration(int); - method @Nullable public android.app.AppOpsManager.OpEventProxyInfo getLastBackgroundProxyInfo(int); - method public long getLastDuration(int); - method public long getLastDuration(int, int, int); - method public long getLastForegroundDuration(int); - method @Nullable public android.app.AppOpsManager.OpEventProxyInfo getLastForegroundProxyInfo(int); - method @Nullable public android.app.AppOpsManager.OpEventProxyInfo getLastProxyInfo(int); - method @Nullable public android.app.AppOpsManager.OpEventProxyInfo getLastProxyInfo(int, int, int); - method public long getLastRejectBackgroundTime(int); - method public long getLastRejectForegroundTime(int); - method public long getLastRejectTime(int); - method public long getLastRejectTime(int, int, int); - method public int getMode(); - method @NonNull public String getOpStr(); - method @Deprecated @Nullable public String getProxyPackageName(); - method @Deprecated @Nullable public String getProxyPackageName(int, int); - method @Deprecated public int getProxyUid(); - method @Deprecated public int getProxyUid(int, int); - method public boolean isRunning(); - method public void writeToParcel(android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.app.AppOpsManager.OpEntry> CREATOR; - } - - public static final class AppOpsManager.OpEventProxyInfo implements android.os.Parcelable { - method public int describeContents(); - method @Nullable public String getAttributionTag(); - method @Nullable public String getPackageName(); - method @IntRange(from=0) public int getUid(); - method public void writeToParcel(@NonNull android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.app.AppOpsManager.OpEventProxyInfo> CREATOR; - } - - public static final class AppOpsManager.PackageOps implements android.os.Parcelable { - method public int describeContents(); - method @NonNull public java.util.List<android.app.AppOpsManager.OpEntry> getOps(); - method @NonNull public String getPackageName(); - method public int getUid(); - method public void writeToParcel(@NonNull android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.app.AppOpsManager.PackageOps> CREATOR; } public class DownloadManager { @@ -458,13 +237,17 @@ package android.app { method @RequiresPermission(android.Manifest.permission.WRITE_DREAM_STATE) public void stopDream(); } + public abstract class HomeVisibilityListener { + ctor public HomeVisibilityListener(); + method public abstract void onHomeVisibilityChanged(boolean); + } + public final class NotificationChannel implements android.os.Parcelable { method public int getOriginalImportance(); method public boolean isBlockable(); method public boolean isImportanceLockedByCriticalDeviceFunction(); method public boolean isImportanceLockedByOEM(); method public void lockFields(int); - method public void setBlockable(boolean); method public void setDeleted(boolean); method public void setDemoted(boolean); method public void setFgServiceShown(boolean); @@ -472,7 +255,6 @@ package android.app { method public void setImportanceLockedByOEM(boolean); method public void setImportantConversation(boolean); method public void setOriginalImportance(int); - field public static final int USER_LOCKED_SOUND = 32; // 0x20 } public final class NotificationChannelGroup implements android.os.Parcelable { @@ -484,12 +266,8 @@ package android.app { public class NotificationManager { method public void allowAssistantAdjustment(String); method public void disallowAssistantAdjustment(String); - method @NonNull public java.util.List<java.lang.String> getAllowedAssistantAdjustments(); - method @Nullable public android.content.ComponentName getAllowedNotificationAssistant(); method public android.content.ComponentName getEffectsSuppressor(); - method public boolean isNotificationAssistantAccessGranted(@NonNull android.content.ComponentName); method public boolean matchesCallFilter(android.os.Bundle); - method public void setNotificationAssistantAccessGranted(@Nullable android.content.ComponentName, boolean); method public void updateNotificationChannel(@NonNull String, int, @NonNull android.app.NotificationChannel); } @@ -499,36 +277,12 @@ package android.app { method public android.graphics.Rect getSourceRectHint(); } - public final class RuntimeAppOpAccessMessage implements android.os.Parcelable { - ctor public RuntimeAppOpAccessMessage(@IntRange(from=0L) int, @IntRange(from=0L) int, @NonNull String, @Nullable String, @NonNull String, int); - method public int describeContents(); - method @Nullable public String getAttributionTag(); - method @NonNull public String getMessage(); - method @NonNull public String getOp(); - method @NonNull public String getPackageName(); - method public int getSamplingStrategy(); - method @IntRange(from=0L) public int getUid(); - method public void writeToParcel(@NonNull android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.app.RuntimeAppOpAccessMessage> CREATOR; - } - public class StatusBarManager { method public void collapsePanels(); method public void expandNotificationsPanel(); - method @NonNull @RequiresPermission(android.Manifest.permission.STATUS_BAR) public android.app.StatusBarManager.DisableInfo getDisableInfo(); - method @RequiresPermission(android.Manifest.permission.STATUS_BAR) public void setDisabledForSetup(boolean); method @RequiresPermission(android.Manifest.permission.STATUS_BAR) public void setExpansionDisabledForSimNetworkLock(boolean); } - public static final class StatusBarManager.DisableInfo { - method public boolean areAllComponentsEnabled(); - method public boolean isNavigateToHomeDisabled(); - method public boolean isNotificationPeekingDisabled(); - method public boolean isRecentsDisabled(); - method public boolean isSearchDisabled(); - method public boolean isStatusBarExpansionDisabled(); - } - public class TaskInfo { method @NonNull public android.content.res.Configuration getConfiguration(); method @NonNull public android.window.WindowContainerToken getToken(); @@ -547,14 +301,12 @@ package android.app { } public class UiModeManager { - method @RequiresPermission(android.Manifest.permission.ENTER_CAR_MODE_PRIORITIZED) public void enableCarMode(@IntRange(from=0) int, int); method public boolean isNightModeLocked(); method public boolean isUiModeLocked(); } public class WallpaperManager { method @Nullable public android.graphics.Bitmap getBitmap(); - method @RequiresPermission(android.Manifest.permission.SET_WALLPAPER_COMPONENT) public boolean setWallpaperComponent(android.content.ComponentName); method public boolean shouldEnableWideColorGamut(); method @RequiresPermission(android.Manifest.permission.READ_EXTERNAL_STORAGE) public boolean wallpaperSupportsWcg(int); } @@ -599,18 +351,13 @@ package android.app { package android.app.admin { public class DevicePolicyManager { - method @Nullable public CharSequence getDeviceOwnerOrganizationName(); method public long getLastBugReportRequestTime(); method public long getLastNetworkLogRetrievalTime(); method public long getLastSecurityLogRetrievalTime(); method public java.util.List<java.lang.String> getOwnerInstalledCaCerts(@NonNull android.os.UserHandle); method public boolean isCurrentInputMethodSetByOwner(); - method public boolean isDeviceManaged(); method public boolean isFactoryResetProtectionPolicySupported(); - field public static final String ACCOUNT_FEATURE_DEVICE_OR_PROFILE_OWNER_ALLOWED = "android.account.DEVICE_OR_PROFILE_OWNER_ALLOWED"; - field public static final String ACCOUNT_FEATURE_DEVICE_OR_PROFILE_OWNER_DISALLOWED = "android.account.DEVICE_OR_PROFILE_OWNER_DISALLOWED"; field public static final String ACTION_DATA_SHARING_RESTRICTION_APPLIED = "android.app.action.DATA_SHARING_RESTRICTION_APPLIED"; - field public static final String EXTRA_RESTRICTION = "android.app.extra.RESTRICTION"; } public static final class SecurityLog.SecurityEvent implements android.os.Parcelable { @@ -619,26 +366,6 @@ package android.app.admin { } -package android.app.assist { - - public static class AssistStructure.ViewNode { - ctor public AssistStructure.ViewNode(); - } - -} - -package android.app.backup { - - public class BackupManager { - method @RequiresPermission(android.Manifest.permission.BACKUP) public android.content.Intent getConfigurationIntent(String); - method @RequiresPermission(android.Manifest.permission.BACKUP) public android.content.Intent getDataManagementIntent(String); - method @Nullable @RequiresPermission(android.Manifest.permission.BACKUP) public CharSequence getDataManagementIntentLabel(@NonNull String); - method @Deprecated @Nullable @RequiresPermission(android.Manifest.permission.BACKUP) public String getDataManagementLabel(@NonNull String); - method @RequiresPermission(android.Manifest.permission.BACKUP) public String getDestinationString(String); - } - -} - package android.app.blob { public class BlobStoreManager { @@ -661,122 +388,21 @@ package android.app.blob { package android.app.prediction { - public final class AppPredictionContext implements android.os.Parcelable { - method public int describeContents(); - method @Nullable public android.os.Bundle getExtras(); - method @NonNull public String getPackageName(); - method @IntRange(from=0) public int getPredictedTargetCount(); - method @NonNull public String getUiSurface(); - method public void writeToParcel(@NonNull android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.app.prediction.AppPredictionContext> CREATOR; - } - - public static final class AppPredictionContext.Builder { - ctor public AppPredictionContext.Builder(@NonNull android.content.Context); - method @NonNull public android.app.prediction.AppPredictionContext build(); - method @NonNull public android.app.prediction.AppPredictionContext.Builder setExtras(@Nullable android.os.Bundle); - method @NonNull public android.app.prediction.AppPredictionContext.Builder setPredictedTargetCount(@IntRange(from=0) int); - method @NonNull public android.app.prediction.AppPredictionContext.Builder setUiSurface(@NonNull String); - } - - public final class AppPredictionManager { - method @NonNull public android.app.prediction.AppPredictor createAppPredictionSession(@NonNull android.app.prediction.AppPredictionContext); - } - - public final class AppPredictionSessionId implements android.os.Parcelable { - method public int describeContents(); - method public void writeToParcel(android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.app.prediction.AppPredictionSessionId> CREATOR; - } - public final class AppPredictor { - method public void destroy(); method public android.app.prediction.AppPredictionSessionId getSessionId(); - method public void notifyAppTargetEvent(@NonNull android.app.prediction.AppTargetEvent); - method public void notifyLaunchLocationShown(@NonNull String, @NonNull java.util.List<android.app.prediction.AppTargetId>); - method public void registerPredictionUpdates(@NonNull java.util.concurrent.Executor, @NonNull android.app.prediction.AppPredictor.Callback); - method public void requestPredictionUpdate(); - method @Nullable public void sortTargets(@NonNull java.util.List<android.app.prediction.AppTarget>, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.util.List<android.app.prediction.AppTarget>>); - method public void unregisterPredictionUpdates(@NonNull android.app.prediction.AppPredictor.Callback); - } - - public static interface AppPredictor.Callback { - method public void onTargetsAvailable(@NonNull java.util.List<android.app.prediction.AppTarget>); - } - - public final class AppTarget implements android.os.Parcelable { - method public int describeContents(); - method @Nullable public String getClassName(); - method @NonNull public android.app.prediction.AppTargetId getId(); - method @NonNull public String getPackageName(); - method @IntRange(from=0) public int getRank(); - method @Nullable public android.content.pm.ShortcutInfo getShortcutInfo(); - method @NonNull public android.os.UserHandle getUser(); - method public void writeToParcel(android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.app.prediction.AppTarget> CREATOR; - } - - public static final class AppTarget.Builder { - ctor public AppTarget.Builder(@NonNull android.app.prediction.AppTargetId, @NonNull String, @NonNull android.os.UserHandle); - ctor public AppTarget.Builder(@NonNull android.app.prediction.AppTargetId, @NonNull android.content.pm.ShortcutInfo); - method @NonNull public android.app.prediction.AppTarget build(); - method @NonNull public android.app.prediction.AppTarget.Builder setClassName(@NonNull String); - method @NonNull public android.app.prediction.AppTarget.Builder setRank(@IntRange(from=0) int); - } - - public final class AppTargetEvent implements android.os.Parcelable { - method public int describeContents(); - method public int getAction(); - method @Nullable public String getLaunchLocation(); - method @Nullable public android.app.prediction.AppTarget getTarget(); - method public void writeToParcel(android.os.Parcel, int); - field public static final int ACTION_DISMISS = 2; // 0x2 - field public static final int ACTION_LAUNCH = 1; // 0x1 - field public static final int ACTION_PIN = 3; // 0x3 - field public static final int ACTION_UNPIN = 4; // 0x4 - field @NonNull public static final android.os.Parcelable.Creator<android.app.prediction.AppTargetEvent> CREATOR; - } - - public static final class AppTargetEvent.Builder { - ctor public AppTargetEvent.Builder(@Nullable android.app.prediction.AppTarget, int); - method @NonNull public android.app.prediction.AppTargetEvent build(); - method @NonNull public android.app.prediction.AppTargetEvent.Builder setLaunchLocation(@Nullable String); - } - - public final class AppTargetId implements android.os.Parcelable { - ctor public AppTargetId(@NonNull String); - method public int describeContents(); - method public void writeToParcel(android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.app.prediction.AppTargetId> CREATOR; } } package android.app.role { - public interface OnRoleHoldersChangedListener { - method public void onRoleHoldersChanged(@NonNull String, @NonNull android.os.UserHandle); - } - public class RoleControllerManager { method @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public void isApplicationVisibleForRole(@NonNull String, @NonNull String, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>); method @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public void isRoleVisible(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>); } public final class RoleManager { - method @RequiresPermission(android.Manifest.permission.OBSERVE_ROLE_HOLDERS) public void addOnRoleHoldersChangedListenerAsUser(@NonNull java.util.concurrent.Executor, @NonNull android.app.role.OnRoleHoldersChangedListener, @NonNull android.os.UserHandle); - method @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public void addRoleHolderAsUser(@NonNull String, @NonNull String, int, @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>); - method @RequiresPermission("com.android.permissioncontroller.permission.MANAGE_ROLES_FROM_CONTROLLER") public boolean addRoleHolderFromController(@NonNull String, @NonNull String); - method @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public void clearRoleHoldersAsUser(@NonNull String, int, @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>); - method @NonNull @RequiresPermission("com.android.permissioncontroller.permission.MANAGE_ROLES_FROM_CONTROLLER") public java.util.List<java.lang.String> getHeldRolesFromController(@NonNull String); - method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public java.util.List<java.lang.String> getRoleHolders(@NonNull String); - method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public java.util.List<java.lang.String> getRoleHoldersAsUser(@NonNull String, @NonNull android.os.UserHandle); method @Nullable public String getSmsRoleHolder(int); - method @RequiresPermission(android.Manifest.permission.OBSERVE_ROLE_HOLDERS) public void removeOnRoleHoldersChangedListenerAsUser(@NonNull android.app.role.OnRoleHoldersChangedListener, @NonNull android.os.UserHandle); - method @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public void removeRoleHolderAsUser(@NonNull String, @NonNull String, int, @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>); - method @RequiresPermission("com.android.permissioncontroller.permission.MANAGE_ROLES_FROM_CONTROLLER") public boolean removeRoleHolderFromController(@NonNull String, @NonNull String); - method @RequiresPermission("com.android.permissioncontroller.permission.MANAGE_ROLES_FROM_CONTROLLER") public void setRoleNamesFromController(@NonNull java.util.List<java.lang.String>); - field public static final int MANAGE_HOLDERS_FLAG_DONT_KILL_APP = 1; // 0x1 } } @@ -795,6 +421,10 @@ package android.app.usage { method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_CRATES) @WorkerThread public java.util.Collection<android.os.storage.CrateInfo> queryCratesForUser(@NonNull java.util.UUID, @NonNull android.os.UserHandle) throws java.io.IOException, android.content.pm.PackageManager.NameNotFoundException; } + public final class UsageStats implements android.os.Parcelable { + ctor public UsageStats(); + } + public final class UsageStatsManager { method public void forceUsageSourceSettingRead(); } @@ -809,23 +439,8 @@ package android.bluetooth { } -package android.companion { - - public final class CompanionDeviceManager { - method @RequiresPermission("android.permission.MANAGE_COMPANION_DEVICES") public boolean isDeviceAssociatedForWifiConnection(@NonNull String, @NonNull android.net.MacAddress, @NonNull android.os.UserHandle); - } - -} - package android.content { - public class ApexEnvironment { - method @NonNull public static android.content.ApexEnvironment getApexEnvironment(@NonNull String); - method @NonNull public java.io.File getCredentialProtectedDataDirForUser(@NonNull android.os.UserHandle); - method @NonNull public java.io.File getDeviceProtectedDataDir(); - method @NonNull public java.io.File getDeviceProtectedDataDirForUser(@NonNull android.os.UserHandle); - } - public final class AutofillOptions implements android.os.Parcelable { ctor public AutofillOptions(int, boolean); method public int describeContents(); @@ -861,36 +476,21 @@ package android.content { method @NonNull public static android.os.UserHandle getUserHandleFromUri(@NonNull android.net.Uri); } - public class ContentProviderClient implements java.lang.AutoCloseable { - method @RequiresPermission(android.Manifest.permission.REMOVE_TASKS) public void setDetectNotResponding(long); - } - public abstract class ContentResolver { - method @NonNull public static android.net.Uri decodeFromFile(@NonNull java.io.File); - method @NonNull public static java.io.File encodeToFile(@NonNull android.net.Uri); method public static String[] getSyncAdapterPackagesForAuthorityAsUser(String, int); } public abstract class Context { - method @NonNull public android.content.Context createContextAsUser(@NonNull android.os.UserHandle, int); - method @NonNull public android.content.Context createPackageContextAsUser(@NonNull String, int, @NonNull android.os.UserHandle) throws android.content.pm.PackageManager.NameNotFoundException; method @NonNull public java.io.File getCrateDir(@NonNull String); method public abstract int getDisplayId(); method public android.os.UserHandle getUser(); method public int getUserId(); method public void setAutofillOptions(@Nullable android.content.AutofillOptions); method public void setContentCaptureOptions(@Nullable android.content.ContentCaptureOptions); - method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public void startActivityAsUser(@NonNull @RequiresPermission android.content.Intent, @NonNull android.os.UserHandle); - field public static final String APP_INTEGRITY_SERVICE = "app_integrity"; - field public static final String BUGREPORT_SERVICE = "bugreport"; field public static final String CONTENT_CAPTURE_MANAGER_SERVICE = "content_capture"; field public static final String DEVICE_IDLE_CONTROLLER = "deviceidle"; field public static final String DREAM_SERVICE = "dream"; - field public static final String ETHERNET_SERVICE = "ethernet"; - field public static final String PERMISSION_SERVICE = "permission"; field public static final String POWER_WHITELIST_MANAGER = "power_whitelist"; - field public static final String ROLLBACK_SERVICE = "rollback"; - field public static final String STATUS_BAR_SERVICE = "statusbar"; field public static final String TEST_NETWORK_SERVICE = "test_network"; } @@ -898,75 +498,13 @@ package android.content { method public int getDisplayId(); } - public class Intent implements java.lang.Cloneable android.os.Parcelable { - field @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public static final String ACTION_MANAGE_DEFAULT_APP = "android.intent.action.MANAGE_DEFAULT_APP"; - field public static final String ACTION_ROLLBACK_COMMITTED = "android.intent.action.ROLLBACK_COMMITTED"; - field public static final String EXTRA_ORIGINATING_UID = "android.intent.extra.ORIGINATING_UID"; - field public static final String EXTRA_ROLE_NAME = "android.intent.extra.ROLE_NAME"; - } - } package android.content.integrity { public class AppIntegrityManager { method @NonNull public android.content.integrity.RuleSet getCurrentRuleSet(); - method @NonNull public String getCurrentRuleSetProvider(); - method @NonNull public String getCurrentRuleSetVersion(); method @NonNull public java.util.List<java.lang.String> getWhitelistedRuleProviders(); - method public void updateRuleSet(@NonNull android.content.integrity.RuleSet, @NonNull android.content.IntentSender); - field public static final String EXTRA_STATUS = "android.content.integrity.extra.STATUS"; - field public static final int STATUS_FAILURE = 1; // 0x1 - field public static final int STATUS_SUCCESS = 0; // 0x0 - } - - public abstract class IntegrityFormula { - method @NonNull public static android.content.integrity.IntegrityFormula all(@NonNull android.content.integrity.IntegrityFormula...); - method @NonNull public static android.content.integrity.IntegrityFormula any(@NonNull android.content.integrity.IntegrityFormula...); - method @NonNull public static android.content.integrity.IntegrityFormula not(@NonNull android.content.integrity.IntegrityFormula); - } - - public static final class IntegrityFormula.Application { - method @NonNull public static android.content.integrity.IntegrityFormula certificatesContain(@NonNull String); - method @NonNull public static android.content.integrity.IntegrityFormula isPreInstalled(); - method @NonNull public static android.content.integrity.IntegrityFormula packageNameEquals(@NonNull String); - method @NonNull public static android.content.integrity.IntegrityFormula versionCodeEquals(@NonNull long); - method @NonNull public static android.content.integrity.IntegrityFormula versionCodeGreaterThan(@NonNull long); - method @NonNull public static android.content.integrity.IntegrityFormula versionCodeGreaterThanOrEqualTo(@NonNull long); - } - - public static final class IntegrityFormula.Installer { - method @NonNull public static android.content.integrity.IntegrityFormula certificatesContain(@NonNull String); - method @NonNull public static android.content.integrity.IntegrityFormula notAllowedByManifest(); - method @NonNull public static android.content.integrity.IntegrityFormula packageNameEquals(@NonNull String); - } - - public static final class IntegrityFormula.SourceStamp { - method @NonNull public static android.content.integrity.IntegrityFormula notTrusted(); - method @NonNull public static android.content.integrity.IntegrityFormula stampCertificateHashEquals(@NonNull String); - } - - public final class Rule implements android.os.Parcelable { - ctor public Rule(@NonNull android.content.integrity.IntegrityFormula, int); - method public int describeContents(); - method public int getEffect(); - method @NonNull public android.content.integrity.IntegrityFormula getFormula(); - method public void writeToParcel(@NonNull android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.content.integrity.Rule> CREATOR; - field public static final int DENY = 0; // 0x0 - field public static final int FORCE_ALLOW = 1; // 0x1 - } - - public class RuleSet { - method @NonNull public java.util.List<android.content.integrity.Rule> getRules(); - method @NonNull public String getVersion(); - } - - public static class RuleSet.Builder { - ctor public RuleSet.Builder(); - method @NonNull public android.content.integrity.RuleSet.Builder addRules(@NonNull java.util.List<android.content.integrity.Rule>); - method @NonNull public android.content.integrity.RuleSet build(); - method @NonNull public android.content.integrity.RuleSet.Builder setVersion(@NonNull String); } } @@ -983,95 +521,38 @@ package android.content.pm { method public boolean isSystemApp(); field public static final int PRIVATE_FLAG_PRIVILEGED = 8; // 0x8 field public int privateFlags; - field public int targetSandboxVersion; } public class LauncherApps { ctor public LauncherApps(android.content.Context); } - public static class PackageInstaller.SessionInfo implements android.os.Parcelable { - method public int getAutoRevokePermissionsMode(); - method public int getRollbackDataPolicy(); - method @NonNull public java.util.Set<java.lang.String> getWhitelistedRestrictedPermissions(); - } - public static class PackageInstaller.SessionParams implements android.os.Parcelable { - method public void setEnableRollback(boolean); - method public void setEnableRollback(boolean, int); - method @RequiresPermission(android.Manifest.permission.INSTALL_GRANT_RUNTIME_PERMISSIONS) public void setGrantedRuntimePermissions(String[]); - method @RequiresPermission(android.Manifest.permission.INSTALL_PACKAGES) public void setInstallAsApex(); - method public void setInstallAsInstantApp(boolean); method public void setInstallerPackageName(@Nullable String); - method public void setRequestDowngrade(boolean); - method @RequiresPermission(android.Manifest.permission.INSTALL_PACKAGES) public void setStaged(); } public abstract class PackageManager { - method @RequiresPermission("android.permission.OBSERVE_GRANT_REVOKE_PERMISSIONS") public abstract void addOnPermissionsChangeListener(@NonNull android.content.pm.PackageManager.OnPermissionsChangedListener); - method public abstract boolean arePermissionsIndividuallyControlled(); method @Nullable public String getContentCaptureServicePackageName(); - method @Nullable @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) public abstract String getDefaultBrowserPackageNameAsUser(int); method @Nullable public String getDefaultTextClassifierPackageName(); - method @Nullable public String getIncidentReportApproverPackageName(); method public abstract int getInstallReason(@NonNull String, @NonNull android.os.UserHandle); method @NonNull public abstract java.util.List<android.content.pm.ApplicationInfo> getInstalledApplicationsAsUser(int, int); - method @NonNull @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) public abstract java.util.List<android.content.pm.PackageInfo> getInstalledPackagesAsUser(int, int); method @Nullable public abstract String[] getNamesForUids(int[]); method @NonNull public abstract String getPermissionControllerPackageName(); - method @android.content.pm.PackageManager.PermissionFlags @RequiresPermission(anyOf={android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS, android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS, android.Manifest.permission.GET_RUNTIME_PERMISSIONS}) public abstract int getPermissionFlags(@NonNull String, @NonNull String, @NonNull android.os.UserHandle); method @NonNull public abstract String getServicesSystemSharedLibraryPackageName(); method @NonNull public abstract String getSharedSystemSharedLibraryPackageName(); method @Nullable public String getSystemTextClassifierPackageName(); method @Nullable public String getWellbeingPackageName(); - method @RequiresPermission(android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS) public abstract void grantRuntimePermission(@NonNull String, @NonNull String, @NonNull android.os.UserHandle); method @RequiresPermission(android.Manifest.permission.INJECT_EVENTS) public void holdLock(int); - method @RequiresPermission("android.permission.OBSERVE_GRANT_REVOKE_PERMISSIONS") public abstract void removeOnPermissionsChangeListener(@NonNull android.content.pm.PackageManager.OnPermissionsChangedListener); - method @RequiresPermission(android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS) public abstract void revokeRuntimePermission(@NonNull String, @NonNull String, @NonNull android.os.UserHandle); - method @RequiresPermission(android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS) public void revokeRuntimePermission(@NonNull String, @NonNull String, @NonNull android.os.UserHandle, @NonNull String); - method @RequiresPermission(anyOf={android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS, android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS}) public abstract void updatePermissionFlags(@NonNull String, @NonNull String, @android.content.pm.PackageManager.PermissionFlags int, @android.content.pm.PackageManager.PermissionFlags int, @NonNull android.os.UserHandle); field public static final String FEATURE_ADOPTABLE_STORAGE = "android.software.adoptable_storage"; field public static final String FEATURE_FILE_BASED_ENCRYPTION = "android.software.file_based_encryption"; - field public static final int FLAG_PERMISSION_APPLY_RESTRICTION = 16384; // 0x4000 - field public static final int FLAG_PERMISSION_GRANTED_BY_DEFAULT = 32; // 0x20 - field public static final int FLAG_PERMISSION_GRANTED_BY_ROLE = 32768; // 0x8000 - field public static final int FLAG_PERMISSION_ONE_TIME = 65536; // 0x10000 - field public static final int FLAG_PERMISSION_POLICY_FIXED = 4; // 0x4 - field public static final int FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT = 2048; // 0x800 - field public static final int FLAG_PERMISSION_RESTRICTION_ROLE_EXEMPT = 262144; // 0x40000 - field public static final int FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT = 4096; // 0x1000 - field public static final int FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT = 8192; // 0x2000 - field public static final int FLAG_PERMISSION_REVIEW_REQUIRED = 64; // 0x40 - field public static final int FLAG_PERMISSION_REVOKED_COMPAT = 8; // 0x8 - field @Deprecated public static final int FLAG_PERMISSION_REVOKE_ON_UPGRADE = 8; // 0x8 field public static final int FLAG_PERMISSION_REVOKE_WHEN_REQUESTED = 128; // 0x80 - field public static final int FLAG_PERMISSION_SYSTEM_FIXED = 16; // 0x10 - field public static final int FLAG_PERMISSION_USER_FIXED = 2; // 0x2 - field public static final int FLAG_PERMISSION_USER_SET = 1; // 0x1 - field public static final int MATCH_FACTORY_ONLY = 2097152; // 0x200000 field public static final int MATCH_KNOWN_PACKAGES = 4202496; // 0x402000 - field public static final int MODULE_APEX_NAME = 1; // 0x1 field public static final String SYSTEM_SHARED_LIBRARY_SERVICES = "android.ext.services"; field public static final String SYSTEM_SHARED_LIBRARY_SHARED = "android.ext.shared"; } - public static interface PackageManager.OnPermissionsChangedListener { - method public void onPermissionsChanged(int); - } - public class PermissionInfo extends android.content.pm.PackageItemInfo implements android.os.Parcelable { - field public static final int FLAG_REMOVED = 2; // 0x2 - field public static final int PROTECTION_FLAG_APP_PREDICTOR = 2097152; // 0x200000 - field public static final int PROTECTION_FLAG_COMPANION = 8388608; // 0x800000 - field public static final int PROTECTION_FLAG_CONFIGURATOR = 524288; // 0x80000 - field public static final int PROTECTION_FLAG_DOCUMENTER = 262144; // 0x40000 - field public static final int PROTECTION_FLAG_INCIDENT_REPORT_APPROVER = 1048576; // 0x100000 - field public static final int PROTECTION_FLAG_OEM = 16384; // 0x4000 - field public static final int PROTECTION_FLAG_RETAIL_DEMO = 16777216; // 0x1000000 - field public static final int PROTECTION_FLAG_SYSTEM_TEXT_CLASSIFIER = 65536; // 0x10000 field public static final int PROTECTION_FLAG_VENDOR_PRIVILEGED = 32768; // 0x8000 - field public static final int PROTECTION_FLAG_WELLBEING = 131072; // 0x20000 - field @Nullable public final String backgroundPermission; } public final class ProviderInfoList implements android.os.Parcelable { @@ -1110,40 +591,11 @@ package android.content.res { package android.content.rollback { - public final class PackageRollbackInfo implements android.os.Parcelable { - method public int describeContents(); - method @NonNull public String getPackageName(); - method @NonNull public android.content.pm.VersionedPackage getVersionRolledBackFrom(); - method @NonNull public android.content.pm.VersionedPackage getVersionRolledBackTo(); - method public void writeToParcel(android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.content.rollback.PackageRollbackInfo> CREATOR; - } - - public final class RollbackInfo implements android.os.Parcelable { - method public int describeContents(); - method @NonNull public java.util.List<android.content.pm.VersionedPackage> getCausePackages(); - method public int getCommittedSessionId(); - method @NonNull public java.util.List<android.content.rollback.PackageRollbackInfo> getPackages(); - method public int getRollbackId(); - method public boolean isStaged(); - method public void writeToParcel(android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.content.rollback.RollbackInfo> CREATOR; - } - public final class RollbackManager { method @RequiresPermission(android.Manifest.permission.TEST_MANAGE_ROLLBACKS) public void blockRollbackManager(long); - method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_ROLLBACKS, android.Manifest.permission.TEST_MANAGE_ROLLBACKS}) public void commitRollback(int, @NonNull java.util.List<android.content.pm.VersionedPackage>, @NonNull android.content.IntentSender); method @RequiresPermission(android.Manifest.permission.TEST_MANAGE_ROLLBACKS) public void expireRollbackForPackage(@NonNull String); - method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_ROLLBACKS, android.Manifest.permission.TEST_MANAGE_ROLLBACKS}) public java.util.List<android.content.rollback.RollbackInfo> getAvailableRollbacks(); - method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_ROLLBACKS, android.Manifest.permission.TEST_MANAGE_ROLLBACKS}) public java.util.List<android.content.rollback.RollbackInfo> getRecentlyCommittedRollbacks(); method @RequiresPermission(android.Manifest.permission.TEST_MANAGE_ROLLBACKS) public void reloadPersistedData(); - field public static final String EXTRA_STATUS = "android.content.rollback.extra.STATUS"; - field public static final String EXTRA_STATUS_MESSAGE = "android.content.rollback.extra.STATUS_MESSAGE"; field public static final String PROPERTY_ROLLBACK_LIFETIME_MILLIS = "rollback_lifetime_in_millis"; - field public static final int STATUS_FAILURE = 1; // 0x1 - field public static final int STATUS_FAILURE_INSTALL = 3; // 0x3 - field public static final int STATUS_FAILURE_ROLLBACK_UNAVAILABLE = 2; // 0x2 - field public static final int STATUS_SUCCESS = 0; // 0x0 } } @@ -1227,20 +679,19 @@ package android.graphics.drawable { package android.hardware.biometrics { public class BiometricManager { - method @NonNull @RequiresPermission(android.Manifest.permission.TEST_BIOMETRIC) public android.hardware.biometrics.BiometricTestSession getTestSession(); + method @NonNull @RequiresPermission(android.Manifest.permission.TEST_BIOMETRIC) public android.hardware.biometrics.BiometricTestSession createTestSession(int); + method @NonNull @RequiresPermission(android.Manifest.permission.TEST_BIOMETRIC) public java.util.List<android.hardware.biometrics.SensorProperties> getSensorProperties(); } public class BiometricTestSession implements java.lang.AutoCloseable { - method @RequiresPermission(android.Manifest.permission.TEST_BIOMETRIC) public void authenticateReject(int, int); - method @RequiresPermission(android.Manifest.permission.TEST_BIOMETRIC) public void authenticateSuccess(int, int); + method @RequiresPermission(android.Manifest.permission.TEST_BIOMETRIC) public void acceptAuthentication(int); + method @RequiresPermission(android.Manifest.permission.TEST_BIOMETRIC) public void cleanupInternalState(int); method @RequiresPermission(android.Manifest.permission.TEST_BIOMETRIC) public void close(); - method @RequiresPermission(android.Manifest.permission.TEST_BIOMETRIC) public void enableTestHal(int, boolean); - method @RequiresPermission(android.Manifest.permission.TEST_BIOMETRIC) public void enrollFinish(int, int); - method @RequiresPermission(android.Manifest.permission.TEST_BIOMETRIC) public void enrollStart(int, int); - method @NonNull @RequiresPermission(android.Manifest.permission.TEST_BIOMETRIC) public java.util.List<android.hardware.biometrics.SensorProperties> getSensorProperties(); - method @RequiresPermission(android.Manifest.permission.TEST_BIOMETRIC) public void internalCleanup(int, int); + method @RequiresPermission(android.Manifest.permission.TEST_BIOMETRIC) public void finishEnroll(int); method @RequiresPermission(android.Manifest.permission.TEST_BIOMETRIC) public void notifyAcquired(int, int); method @RequiresPermission(android.Manifest.permission.TEST_BIOMETRIC) public void notifyError(int, int); + method @RequiresPermission(android.Manifest.permission.TEST_BIOMETRIC) public void rejectAuthentication(int); + method @RequiresPermission(android.Manifest.permission.TEST_BIOMETRIC) public void startEnroll(int); } public class SensorProperties { @@ -1255,13 +706,6 @@ package android.hardware.biometrics { package android.hardware.camera2 { - public abstract class CameraDevice implements java.lang.AutoCloseable { - method @Deprecated public abstract void createCustomCaptureSession(android.hardware.camera2.params.InputConfiguration, @NonNull java.util.List<android.hardware.camera2.params.OutputConfiguration>, int, @NonNull android.hardware.camera2.CameraCaptureSession.StateCallback, @Nullable android.os.Handler) throws android.hardware.camera2.CameraAccessException; - field public static final int SESSION_OPERATION_MODE_CONSTRAINED_HIGH_SPEED = 1; // 0x1 - field public static final int SESSION_OPERATION_MODE_NORMAL = 0; // 0x0 - field public static final int SESSION_OPERATION_MODE_VENDOR_START = 32768; // 0x8000 - } - public final class CameraManager { method public String[] getCameraIdListNoLazy() throws android.hardware.camera2.CameraAccessException; } @@ -1270,15 +714,6 @@ package android.hardware.camera2 { package android.hardware.display { - public final class AmbientBrightnessDayStats implements android.os.Parcelable { - method public int describeContents(); - method public float[] getBucketBoundaries(); - method public java.time.LocalDate getLocalDate(); - method public float[] getStats(); - method public void writeToParcel(android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.hardware.display.AmbientBrightnessDayStats> CREATOR; - } - public class AmbientDisplayConfiguration { ctor public AmbientDisplayConfiguration(android.content.Context); method public boolean alwaysOnAvailable(); @@ -1286,70 +721,8 @@ package android.hardware.display { method public boolean alwaysOnEnabled(int); } - public final class BrightnessChangeEvent implements android.os.Parcelable { - method public int describeContents(); - method public void writeToParcel(android.os.Parcel, int); - field @NonNull 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 long colorSampleDuration; - field public final int colorTemperature; - field @Nullable public final long[] colorValueBuckets; - field public final boolean isDefaultBrightnessConfig; - field public final boolean isUserSetBrightness; - field public final float lastBrightness; - field public final long[] luxTimestamps; - field public final float[] luxValues; - field public final boolean nightMode; - field public final String packageName; - field public final float powerBrightnessFactor; - field public final long timeStamp; - } - - public final class BrightnessConfiguration implements android.os.Parcelable { - method public int describeContents(); - method @Nullable public android.hardware.display.BrightnessCorrection getCorrectionByCategory(int); - method @Nullable public android.hardware.display.BrightnessCorrection getCorrectionByPackageName(@NonNull String); - method public android.util.Pair<float[],float[]> getCurve(); - method public float getShortTermModelLowerLuxMultiplier(); - method public long getShortTermModelTimeoutMillis(); - method public float getShortTermModelUpperLuxMultiplier(); - method public boolean shouldCollectColorSamples(); - method public void writeToParcel(android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.hardware.display.BrightnessConfiguration> CREATOR; - field public static final long SHORT_TERM_TIMEOUT_UNSET = -1L; // 0xffffffffffffffffL - } - - public static class BrightnessConfiguration.Builder { - ctor public BrightnessConfiguration.Builder(float[], float[]); - method @NonNull public android.hardware.display.BrightnessConfiguration.Builder addCorrectionByCategory(int, @NonNull android.hardware.display.BrightnessCorrection); - method @NonNull public android.hardware.display.BrightnessConfiguration.Builder addCorrectionByPackageName(@NonNull String, @NonNull android.hardware.display.BrightnessCorrection); - method @NonNull public android.hardware.display.BrightnessConfiguration build(); - method public int getMaxCorrectionsByCategory(); - method public int getMaxCorrectionsByPackageName(); - method @NonNull public android.hardware.display.BrightnessConfiguration.Builder setDescription(@Nullable String); - method @NonNull public android.hardware.display.BrightnessConfiguration.Builder setShortTermModelLowerLuxMultiplier(@FloatRange(from=0.0f) float); - method @NonNull public android.hardware.display.BrightnessConfiguration.Builder setShortTermModelTimeoutMillis(long); - method @NonNull public android.hardware.display.BrightnessConfiguration.Builder setShortTermModelUpperLuxMultiplier(@FloatRange(from=0.0f) float); - method @NonNull public android.hardware.display.BrightnessConfiguration.Builder setShouldCollectColorSamples(boolean); - } - - public final class BrightnessCorrection implements android.os.Parcelable { - method @FloatRange(from=0.0) public float apply(@FloatRange(from=0.0) float); - method @NonNull public static android.hardware.display.BrightnessCorrection createScaleAndTranslateLog(float, float); - method public int describeContents(); - method public void writeToParcel(android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.hardware.display.BrightnessCorrection> CREATOR; - } - public final class DisplayManager { - method @RequiresPermission(android.Manifest.permission.ACCESS_AMBIENT_LIGHT_STATS) public java.util.List<android.hardware.display.AmbientBrightnessDayStats> getAmbientBrightnessStats(); - method @RequiresPermission(android.Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS) public android.hardware.display.BrightnessConfiguration getBrightnessConfiguration(); - method @RequiresPermission(android.Manifest.permission.BRIGHTNESS_SLIDER_USAGE) public java.util.List<android.hardware.display.BrightnessChangeEvent> getBrightnessEvents(); - method @Nullable @RequiresPermission(android.Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS) public android.hardware.display.BrightnessConfiguration getDefaultBrightnessConfiguration(); - method public android.graphics.Point getStableDisplaySize(); method public boolean isMinimalPostProcessingRequested(int); - method @RequiresPermission(android.Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS) public void setBrightnessConfiguration(android.hardware.display.BrightnessConfiguration); method @RequiresPermission(android.Manifest.permission.OVERRIDE_DISPLAY_MODE_REQUESTS) public void setShouldAlwaysRespectAppRequestedMode(boolean); method @RequiresPermission(android.Manifest.permission.OVERRIDE_DISPLAY_MODE_REQUESTS) public boolean shouldAlwaysRespectAppRequestedMode(); field public static final int VIRTUAL_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS = 512; // 0x200 @@ -1361,104 +734,14 @@ package android.hardware.display { package android.hardware.fingerprint { @Deprecated public class FingerprintManager { - method @Deprecated @NonNull @RequiresPermission(android.Manifest.permission.TEST_BIOMETRIC) public android.hardware.biometrics.BiometricTestSession getTestSession(); + method @Deprecated @NonNull @RequiresPermission(android.Manifest.permission.TEST_BIOMETRIC) public android.hardware.biometrics.BiometricTestSession createTestSession(int); + method @Deprecated @NonNull @RequiresPermission(android.Manifest.permission.TEST_BIOMETRIC) public java.util.List<android.hardware.biometrics.SensorProperties> getSensorProperties(); } } package android.hardware.hdmi { - public final class HdmiControlManager { - method @Nullable public android.hardware.hdmi.HdmiSwitchClient getSwitchClient(); - method @RequiresPermission(android.Manifest.permission.HDMI_CEC) public void setStandbyMode(boolean); - field public static final String ACTION_OSD_MESSAGE = "android.hardware.hdmi.action.OSD_MESSAGE"; - field public static final int AVR_VOLUME_MUTED = 101; // 0x65 - field public static final int CLEAR_TIMER_STATUS_CEC_DISABLE = 162; // 0xa2 - field public static final int CLEAR_TIMER_STATUS_CHECK_RECORDER_CONNECTION = 160; // 0xa0 - field public static final int CLEAR_TIMER_STATUS_FAIL_TO_CLEAR_SELECTED_SOURCE = 161; // 0xa1 - field public static final int CLEAR_TIMER_STATUS_TIMER_CLEARED = 128; // 0x80 - field public static final int CLEAR_TIMER_STATUS_TIMER_NOT_CLEARED_NO_INFO_AVAILABLE = 2; // 0x2 - field public static final int CLEAR_TIMER_STATUS_TIMER_NOT_CLEARED_NO_MATCHING = 1; // 0x1 - field public static final int CLEAR_TIMER_STATUS_TIMER_NOT_CLEARED_RECORDING = 0; // 0x0 - field public static final int CONTROL_STATE_CHANGED_REASON_SETTING = 1; // 0x1 - field public static final int CONTROL_STATE_CHANGED_REASON_STANDBY = 3; // 0x3 - field public static final int CONTROL_STATE_CHANGED_REASON_START = 0; // 0x0 - field public static final int CONTROL_STATE_CHANGED_REASON_WAKEUP = 2; // 0x2 - field public static final int DEVICE_EVENT_ADD_DEVICE = 1; // 0x1 - field public static final int DEVICE_EVENT_REMOVE_DEVICE = 2; // 0x2 - field public static final int DEVICE_EVENT_UPDATE_DEVICE = 3; // 0x3 - field public static final String EXTRA_MESSAGE_EXTRA_PARAM1 = "android.hardware.hdmi.extra.MESSAGE_EXTRA_PARAM1"; - field public static final String EXTRA_MESSAGE_ID = "android.hardware.hdmi.extra.MESSAGE_ID"; - field public static final int ONE_TOUCH_RECORD_ALREADY_RECORDING = 18; // 0x12 - field public static final int ONE_TOUCH_RECORD_CEC_DISABLED = 51; // 0x33 - field public static final int ONE_TOUCH_RECORD_CHECK_RECORDER_CONNECTION = 49; // 0x31 - field public static final int ONE_TOUCH_RECORD_DISALLOW_TO_COPY = 13; // 0xd - field public static final int ONE_TOUCH_RECORD_DISALLOW_TO_FUTHER_COPIES = 14; // 0xe - field public static final int ONE_TOUCH_RECORD_FAIL_TO_RECORD_DISPLAYED_SCREEN = 50; // 0x32 - field public static final int ONE_TOUCH_RECORD_INVALID_EXTERNAL_PHYSICAL_ADDRESS = 10; // 0xa - field public static final int ONE_TOUCH_RECORD_INVALID_EXTERNAL_PLUG_NUMBER = 9; // 0x9 - field public static final int ONE_TOUCH_RECORD_MEDIA_PROBLEM = 21; // 0x15 - field public static final int ONE_TOUCH_RECORD_MEDIA_PROTECTED = 19; // 0x13 - field public static final int ONE_TOUCH_RECORD_NOT_ENOUGH_SPACE = 22; // 0x16 - field public static final int ONE_TOUCH_RECORD_NO_MEDIA = 16; // 0x10 - field public static final int ONE_TOUCH_RECORD_NO_OR_INSUFFICIENT_CA_ENTITLEMENTS = 12; // 0xc - field public static final int ONE_TOUCH_RECORD_NO_SOURCE_SIGNAL = 20; // 0x14 - field public static final int ONE_TOUCH_RECORD_OTHER_REASON = 31; // 0x1f - field public static final int ONE_TOUCH_RECORD_PARENT_LOCK_ON = 23; // 0x17 - field public static final int ONE_TOUCH_RECORD_PLAYING = 17; // 0x11 - field public static final int ONE_TOUCH_RECORD_PREVIOUS_RECORDING_IN_PROGRESS = 48; // 0x30 - field public static final int ONE_TOUCH_RECORD_RECORDING_ALREADY_TERMINATED = 27; // 0x1b - field public static final int ONE_TOUCH_RECORD_RECORDING_ANALOGUE_SERVICE = 3; // 0x3 - field public static final int ONE_TOUCH_RECORD_RECORDING_CURRENTLY_SELECTED_SOURCE = 1; // 0x1 - field public static final int ONE_TOUCH_RECORD_RECORDING_DIGITAL_SERVICE = 2; // 0x2 - field public static final int ONE_TOUCH_RECORD_RECORDING_EXTERNAL_INPUT = 4; // 0x4 - field public static final int ONE_TOUCH_RECORD_RECORDING_TERMINATED_NORMALLY = 26; // 0x1a - field public static final int ONE_TOUCH_RECORD_UNABLE_ANALOGUE_SERVICE = 6; // 0x6 - field public static final int ONE_TOUCH_RECORD_UNABLE_DIGITAL_SERVICE = 5; // 0x5 - field public static final int ONE_TOUCH_RECORD_UNABLE_SELECTED_SERVICE = 7; // 0x7 - field public static final int ONE_TOUCH_RECORD_UNSUPPORTED_CA = 11; // 0xb - field public static final int OSD_MESSAGE_ARC_CONNECTED_INVALID_PORT = 1; // 0x1 - field public static final int OSD_MESSAGE_AVR_VOLUME_CHANGED = 2; // 0x2 - field public static final int POWER_STATUS_ON = 0; // 0x0 - field public static final int POWER_STATUS_STANDBY = 1; // 0x1 - field public static final int POWER_STATUS_TRANSIENT_TO_ON = 2; // 0x2 - field public static final int POWER_STATUS_TRANSIENT_TO_STANDBY = 3; // 0x3 - field public static final int POWER_STATUS_UNKNOWN = -1; // 0xffffffff - field @Deprecated public static final int RESULT_ALREADY_IN_PROGRESS = 4; // 0x4 - field public static final int RESULT_COMMUNICATION_FAILED = 7; // 0x7 - field public static final int RESULT_EXCEPTION = 5; // 0x5 - field public static final int RESULT_INCORRECT_MODE = 6; // 0x6 - field public static final int RESULT_SOURCE_NOT_AVAILABLE = 2; // 0x2 - field public static final int RESULT_SUCCESS = 0; // 0x0 - field public static final int RESULT_TARGET_NOT_AVAILABLE = 3; // 0x3 - field public static final int RESULT_TIMEOUT = 1; // 0x1 - field public static final int TIMER_RECORDING_RESULT_EXTRA_CEC_DISABLED = 3; // 0x3 - field public static final int TIMER_RECORDING_RESULT_EXTRA_CHECK_RECORDER_CONNECTION = 1; // 0x1 - field public static final int TIMER_RECORDING_RESULT_EXTRA_FAIL_TO_RECORD_SELECTED_SOURCE = 2; // 0x2 - field public static final int TIMER_RECORDING_RESULT_EXTRA_NO_ERROR = 0; // 0x0 - field public static final int TIMER_RECORDING_TYPE_ANALOGUE = 2; // 0x2 - field public static final int TIMER_RECORDING_TYPE_DIGITAL = 1; // 0x1 - field public static final int TIMER_RECORDING_TYPE_EXTERNAL = 3; // 0x3 - field public static final int TIMER_STATUS_MEDIA_INFO_NOT_PRESENT = 2; // 0x2 - field public static final int TIMER_STATUS_MEDIA_INFO_PRESENT_NOT_PROTECTED = 0; // 0x0 - field public static final int TIMER_STATUS_MEDIA_INFO_PRESENT_PROTECTED = 1; // 0x1 - field public static final int TIMER_STATUS_NOT_PROGRAMMED_CA_NOT_SUPPORTED = 6; // 0x6 - field public static final int TIMER_STATUS_NOT_PROGRAMMED_CLOCK_FAILURE = 10; // 0xa - field public static final int TIMER_STATUS_NOT_PROGRAMMED_DATE_OUT_OF_RANGE = 2; // 0x2 - field public static final int TIMER_STATUS_NOT_PROGRAMMED_DUPLICATED = 14; // 0xe - field public static final int TIMER_STATUS_NOT_PROGRAMMED_INVALID_EXTERNAL_PHYSICAL_NUMBER = 5; // 0x5 - field public static final int TIMER_STATUS_NOT_PROGRAMMED_INVALID_EXTERNAL_PLUG_NUMBER = 4; // 0x4 - field public static final int TIMER_STATUS_NOT_PROGRAMMED_INVALID_SEQUENCE = 3; // 0x3 - field public static final int TIMER_STATUS_NOT_PROGRAMMED_NO_CA_ENTITLEMENTS = 7; // 0x7 - field public static final int TIMER_STATUS_NOT_PROGRAMMED_NO_FREE_TIME = 1; // 0x1 - field public static final int TIMER_STATUS_NOT_PROGRAMMED_PARENTAL_LOCK_ON = 9; // 0x9 - field public static final int TIMER_STATUS_NOT_PROGRAMMED_UNSUPPORTED_RESOLUTION = 8; // 0x8 - field public static final int TIMER_STATUS_PROGRAMMED_INFO_ENOUGH_SPACE = 8; // 0x8 - field public static final int TIMER_STATUS_PROGRAMMED_INFO_MIGHT_NOT_ENOUGH_SPACE = 11; // 0xb - field public static final int TIMER_STATUS_PROGRAMMED_INFO_NOT_ENOUGH_SPACE = 9; // 0x9 - field public static final int TIMER_STATUS_PROGRAMMED_INFO_NO_MEDIA_INFO = 10; // 0xa - } - public final class HdmiControlServiceWrapper { ctor public HdmiControlServiceWrapper(); method @NonNull public android.hardware.hdmi.HdmiControlManager createHdmiControlManager(); @@ -1467,25 +750,6 @@ package android.hardware.hdmi { field public static final int DEVICE_PURE_CEC_SWITCH = 6; // 0x6 } - public final class HdmiPortInfo implements android.os.Parcelable { - ctor public HdmiPortInfo(int, int, int, boolean, boolean, boolean); - method public int describeContents(); - method public int getAddress(); - method public int getId(); - method public int getType(); - method public boolean isArcSupported(); - method public boolean isCecSupported(); - method public boolean isMhlSupported(); - field @NonNull public static final android.os.Parcelable.Creator<android.hardware.hdmi.HdmiPortInfo> CREATOR; - field public static final int PORT_INPUT = 0; // 0x0 - field public static final int PORT_OUTPUT = 1; // 0x1 - } - - public class HdmiSwitchClient extends android.hardware.hdmi.HdmiClient { - method public int getDeviceType(); - method @NonNull public java.util.List<android.hardware.hdmi.HdmiPortInfo> getPortInfo(); - } - } package android.hardware.input { @@ -1502,43 +766,8 @@ package android.hardware.input { package android.hardware.lights { - public final class Light implements android.os.Parcelable { - method public int describeContents(); - method public int getId(); - method public int getOrdinal(); - method public int getType(); - method public void writeToParcel(@NonNull android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.hardware.lights.Light> CREATOR; - } - - public final class LightState implements android.os.Parcelable { - ctor public LightState(@ColorInt int); - method public int describeContents(); - method @ColorInt public int getColor(); - method public void writeToParcel(@NonNull android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.hardware.lights.LightState> CREATOR; - } - public final class LightsManager { method @NonNull @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_LIGHTS) public android.hardware.lights.LightState getLightState(@NonNull android.hardware.lights.Light); - method @NonNull @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_LIGHTS) public java.util.List<android.hardware.lights.Light> getLights(); - method @NonNull @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_LIGHTS) public android.hardware.lights.LightsManager.LightsSession openSession(); - field public static final int LIGHT_TYPE_MICROPHONE = 8; // 0x8 - } - - public final class LightsManager.LightsSession implements java.lang.AutoCloseable { - method @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_LIGHTS) public void close(); - method @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_LIGHTS) public void requestLights(@NonNull android.hardware.lights.LightsRequest); - } - - public final class LightsRequest { - } - - public static final class LightsRequest.Builder { - ctor public LightsRequest.Builder(); - method @NonNull public android.hardware.lights.LightsRequest build(); - method @NonNull public android.hardware.lights.LightsRequest.Builder clearLight(@NonNull android.hardware.lights.Light); - method @NonNull public android.hardware.lights.LightsRequest.Builder setLight(@NonNull android.hardware.lights.Light, @NonNull android.hardware.lights.LightState); } } @@ -1569,84 +798,16 @@ package android.hardware.soundtrigger { field @NonNull public static final android.os.Parcelable.Creator<android.hardware.soundtrigger.KeyphraseMetadata> CREATOR; } - public class SoundTrigger { - field public static final int RECOGNITION_MODE_GENERIC = 8; // 0x8 - field public static final int RECOGNITION_MODE_USER_AUTHENTICATION = 4; // 0x4 - field public static final int RECOGNITION_MODE_USER_IDENTIFICATION = 2; // 0x2 - field public static final int RECOGNITION_MODE_VOICE_TRIGGER = 1; // 0x1 - field public static final int STATUS_OK = 0; // 0x0 - } - - public static final class SoundTrigger.Keyphrase implements android.os.Parcelable { - ctor public SoundTrigger.Keyphrase(int, int, @NonNull java.util.Locale, @NonNull String, @Nullable int[]); - method public int getId(); - method @NonNull public java.util.Locale getLocale(); - method public int getRecognitionModes(); - method @NonNull public String getText(); - method @NonNull public int[] getUsers(); - method @NonNull public static android.hardware.soundtrigger.SoundTrigger.Keyphrase readFromParcel(@NonNull android.os.Parcel); - method public void writeToParcel(@NonNull android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.hardware.soundtrigger.SoundTrigger.Keyphrase> CREATOR; - } - - public static final class SoundTrigger.KeyphraseSoundModel extends android.hardware.soundtrigger.SoundTrigger.SoundModel implements android.os.Parcelable { - ctor public SoundTrigger.KeyphraseSoundModel(@NonNull java.util.UUID, @NonNull java.util.UUID, @Nullable byte[], @Nullable android.hardware.soundtrigger.SoundTrigger.Keyphrase[], int); - ctor public SoundTrigger.KeyphraseSoundModel(@NonNull java.util.UUID, @NonNull java.util.UUID, @Nullable byte[], @Nullable android.hardware.soundtrigger.SoundTrigger.Keyphrase[]); - method @NonNull public android.hardware.soundtrigger.SoundTrigger.Keyphrase[] getKeyphrases(); - method @NonNull public static android.hardware.soundtrigger.SoundTrigger.KeyphraseSoundModel readFromParcel(@NonNull android.os.Parcel); - method public void writeToParcel(@NonNull android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.hardware.soundtrigger.SoundTrigger.KeyphraseSoundModel> CREATOR; - } - public static final class SoundTrigger.ModelParamRange implements android.os.Parcelable { ctor public SoundTrigger.ModelParamRange(int, int); - method public int getEnd(); - method public int getStart(); - method public void writeToParcel(@NonNull android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.hardware.soundtrigger.SoundTrigger.ModelParamRange> CREATOR; } public static final class SoundTrigger.ModuleProperties implements android.os.Parcelable { ctor public SoundTrigger.ModuleProperties(int, @NonNull String, @NonNull String, @NonNull String, int, @NonNull String, int, int, int, int, boolean, int, boolean, int, boolean, int); - method public int describeContents(); - method public int getAudioCapabilities(); - method @NonNull public String getDescription(); - method public int getId(); - method @NonNull public String getImplementor(); - method public int getMaxBufferMillis(); - method public int getMaxKeyphrases(); - method public int getMaxSoundModels(); - method public int getMaxUsers(); - method public int getPowerConsumptionMw(); - method public int getRecognitionModes(); - method @NonNull public String getSupportedModelArch(); - method @NonNull public java.util.UUID getUuid(); - method public int getVersion(); - method public boolean isCaptureTransitionSupported(); - method public boolean isConcurrentCaptureSupported(); - method public boolean isTriggerReturnedInEvent(); - method public void writeToParcel(android.os.Parcel, int); - field public static final int AUDIO_CAPABILITY_ECHO_CANCELLATION = 1; // 0x1 - field public static final int AUDIO_CAPABILITY_NOISE_SUPPRESSION = 2; // 0x2 - field @NonNull public static final android.os.Parcelable.Creator<android.hardware.soundtrigger.SoundTrigger.ModuleProperties> CREATOR; } public static class SoundTrigger.RecognitionEvent { ctor public SoundTrigger.RecognitionEvent(int, int, boolean, int, int, int, boolean, @NonNull android.media.AudioFormat, @Nullable byte[]); - method @Nullable public android.media.AudioFormat getCaptureFormat(); - method public int getCaptureSession(); - method public byte[] getData(); - method public boolean isCaptureAvailable(); - } - - public static class SoundTrigger.SoundModel { - method @NonNull public byte[] getData(); - method public int getType(); - method @NonNull public java.util.UUID getUuid(); - method @NonNull public java.util.UUID getVendorUuid(); - method public int getVersion(); - field public static final int TYPE_GENERIC_SOUND = 1; // 0x1 - field public static final int TYPE_KEYPHRASE = 0; // 0x0 } } @@ -1745,59 +906,17 @@ package android.location { method public void setType(int); } - public class Location implements android.os.Parcelable { - method public void makeComplete(); - field @Deprecated public static final String EXTRA_NO_GPS_LOCATION = "noGPSLocation"; - } - public class LocationManager { method @NonNull public String[] getBackgroundThrottlingWhitelist(); - method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void getCurrentLocation(@NonNull android.location.LocationRequest, @Nullable android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.location.Location>); method @NonNull public String[] getIgnoreSettingsWhitelist(); method @Deprecated @Nullable @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public java.util.List<java.lang.String> getProviderPackages(@NonNull String); - method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(@Nullable android.location.LocationRequest, @NonNull android.location.LocationListener, @Nullable android.os.Looper); - method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(@Nullable android.location.LocationRequest, @NonNull java.util.concurrent.Executor, @NonNull android.location.LocationListener); - method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void requestLocationUpdates(@Nullable android.location.LocationRequest, @NonNull android.app.PendingIntent); - method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void setLocationEnabledForUser(boolean, @NonNull android.os.UserHandle); field public static final String FUSED_PROVIDER = "fused"; } - public final class LocationRequest implements android.os.Parcelable { - method @NonNull public android.os.WorkSource getWorkSource(); - method public boolean isHiddenFromAppOps(); - method public boolean isLocationSettingsIgnored(); - method public boolean isLowPower(); - field public static final int ACCURACY_BLOCK = 102; // 0x66 - field public static final int ACCURACY_CITY = 104; // 0x68 - field public static final int ACCURACY_FINE = 100; // 0x64 - field public static final int POWER_HIGH = 203; // 0xcb - field public static final int POWER_LOW = 201; // 0xc9 - } - - public static final class LocationRequest.Builder { - method @NonNull @RequiresPermission(android.Manifest.permission.UPDATE_APP_OPS_STATS) public android.location.LocationRequest.Builder setHiddenFromAppOps(boolean); - method @NonNull @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public android.location.LocationRequest.Builder setLocationSettingsIgnored(boolean); - method @NonNull @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public android.location.LocationRequest.Builder setLowPower(boolean); - method @NonNull @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public android.location.LocationRequest.Builder setWorkSource(@Nullable android.os.WorkSource); - } - } package android.media { - public final class AudioFocusInfo implements android.os.Parcelable { - method public int describeContents(); - method @NonNull public android.media.AudioAttributes getAttributes(); - method @NonNull public String getClientId(); - method public int getClientUid(); - method public int getFlags(); - method public int getGainRequest(); - method public int getLossReceived(); - method @NonNull public String getPackageName(); - method public void writeToParcel(android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.media.AudioFocusInfo> CREATOR; - } - public final class AudioFocusRequest { method @Nullable public android.media.AudioManager.OnAudioFocusChangeListener getOnAudioFocusChangeListener(); } @@ -1810,13 +929,7 @@ package android.media { } public class AudioManager { - method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int dispatchAudioFocusChange(@NonNull android.media.AudioFocusInfo, int, @NonNull android.media.audiopolicy.AudioPolicy); method public boolean hasRegisteredDynamicPolicy(); - method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int registerAudioPolicy(@NonNull android.media.audiopolicy.AudioPolicy); - method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void setFocusRequestResult(@NonNull android.media.AudioFocusInfo, int, @NonNull android.media.audiopolicy.AudioPolicy); - method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void unregisterAudioPolicy(@NonNull android.media.audiopolicy.AudioPolicy); - method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void unregisterAudioPolicyAsync(@NonNull android.media.audiopolicy.AudioPolicy); - field public static final int SUCCESS = 0; // 0x0 } public static final class AudioRecord.MetricsConstants { @@ -1872,68 +985,6 @@ package android.media { method @NonNull public String getOriginalId(); } - public final class MediaTranscodeManager { - method @NonNull public android.media.MediaTranscodeManager.TranscodingJob enqueueRequest(@NonNull android.media.MediaTranscodeManager.TranscodingRequest, @NonNull java.util.concurrent.Executor, @NonNull android.media.MediaTranscodeManager.OnTranscodingFinishedListener) throws java.io.FileNotFoundException, android.media.MediaTranscodingException.ServiceNotAvailableException; - field public static final int PRIORITY_REALTIME = 1; // 0x1 - field public static final int TRANSCODING_TYPE_VIDEO = 1; // 0x1 - } - - @java.lang.FunctionalInterface public static interface MediaTranscodeManager.OnTranscodingFinishedListener { - method public void onTranscodingFinished(@NonNull android.media.MediaTranscodeManager.TranscodingJob); - } - - public static final class MediaTranscodeManager.TranscodingJob { - method public void cancel(); - method public int getJobId(); - method @IntRange(from=0, to=100) public int getProgress(); - method public int getResult(); - method public int getStatus(); - method public void setOnProgressUpdateListener(@NonNull java.util.concurrent.Executor, @Nullable android.media.MediaTranscodeManager.TranscodingJob.OnProgressUpdateListener); - method public void setOnProgressUpdateListener(int, @NonNull java.util.concurrent.Executor, @Nullable android.media.MediaTranscodeManager.TranscodingJob.OnProgressUpdateListener); - field public static final int RESULT_CANCELED = 4; // 0x4 - field public static final int RESULT_ERROR = 3; // 0x3 - field public static final int RESULT_NONE = 1; // 0x1 - field public static final int RESULT_SUCCESS = 2; // 0x2 - field public static final int STATUS_FINISHED = 3; // 0x3 - field public static final int STATUS_PAUSED = 4; // 0x4 - field public static final int STATUS_PENDING = 1; // 0x1 - field public static final int STATUS_RUNNING = 2; // 0x2 - } - - @java.lang.FunctionalInterface public static interface MediaTranscodeManager.TranscodingJob.OnProgressUpdateListener { - method public void onProgressUpdate(@NonNull android.media.MediaTranscodeManager.TranscodingJob, @IntRange(from=0, to=100) int); - } - - public static final class MediaTranscodeManager.TranscodingRequest { - method public int getClientPid(); - method public int getClientUid(); - method @NonNull public android.net.Uri getDestinationUri(); - method public int getPriority(); - method @NonNull public android.net.Uri getSourceUri(); - method public int getType(); - method @Nullable public android.media.MediaFormat getVideoTrackFormat(); - } - - public static final class MediaTranscodeManager.TranscodingRequest.Builder { - ctor public MediaTranscodeManager.TranscodingRequest.Builder(); - method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest build(); - method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.Builder setClientPid(int); - method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.Builder setClientUid(int); - method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.Builder setDestinationUri(@NonNull android.net.Uri); - method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.Builder setPriority(int); - method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.Builder setSourceUri(@NonNull android.net.Uri); - method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.Builder setType(int); - method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.Builder setVideoTrackFormat(@NonNull android.media.MediaFormat); - } - - public static class MediaTranscodeManager.TranscodingRequest.MediaFormatResolver { - ctor public MediaTranscodeManager.TranscodingRequest.MediaFormatResolver(); - method @Nullable public android.media.MediaFormat resolveVideoFormat(); - method @NonNull public android.media.MediaTranscodeManager.TranscodingRequest.MediaFormatResolver setSourceVideoFormatHint(@NonNull android.media.MediaFormat); - method public boolean shouldTranscode(); - field public static final String CAPS_SUPPORTS_HEVC = "support-hevc"; - } - public final class PlaybackParams implements android.os.Parcelable { method public int getAudioStretchMode(); method public android.media.PlaybackParams setAudioStretchMode(int); @@ -1982,141 +1033,8 @@ package android.media.audiofx { package android.media.audiopolicy { - public class AudioMix { - method public int getMixState(); - field public static final int MIX_STATE_DISABLED = -1; // 0xffffffff - field public static final int MIX_STATE_IDLE = 0; // 0x0 - field public static final int MIX_STATE_MIXING = 1; // 0x1 - field public static final int ROUTE_FLAG_LOOP_BACK = 2; // 0x2 - field public static final int ROUTE_FLAG_RENDER = 1; // 0x1 - } - - public static class AudioMix.Builder { - ctor public AudioMix.Builder(android.media.audiopolicy.AudioMixingRule) throws java.lang.IllegalArgumentException; - method public android.media.audiopolicy.AudioMix build() throws java.lang.IllegalArgumentException; - method public android.media.audiopolicy.AudioMix.Builder setDevice(@NonNull android.media.AudioDeviceInfo) throws java.lang.IllegalArgumentException; - method public android.media.audiopolicy.AudioMix.Builder setFormat(android.media.AudioFormat) throws java.lang.IllegalArgumentException; - method public android.media.audiopolicy.AudioMix.Builder setRouteFlags(int) throws java.lang.IllegalArgumentException; - } - - public class AudioMixingRule { - field public static final int RULE_MATCH_ATTRIBUTE_CAPTURE_PRESET = 2; // 0x2 - field public static final int RULE_MATCH_ATTRIBUTE_USAGE = 1; // 0x1 - field public static final int RULE_MATCH_UID = 4; // 0x4 - field public static final int RULE_MATCH_USERID = 8; // 0x8 - } - - public static class AudioMixingRule.Builder { - ctor public AudioMixingRule.Builder(); - method public android.media.audiopolicy.AudioMixingRule.Builder addMixRule(int, Object) throws java.lang.IllegalArgumentException; - method public android.media.audiopolicy.AudioMixingRule.Builder addRule(android.media.AudioAttributes, int) throws java.lang.IllegalArgumentException; - method @NonNull public android.media.audiopolicy.AudioMixingRule.Builder allowPrivilegedPlaybackCapture(boolean); - method public android.media.audiopolicy.AudioMixingRule build(); - method public android.media.audiopolicy.AudioMixingRule.Builder excludeMixRule(int, Object) throws java.lang.IllegalArgumentException; - method public android.media.audiopolicy.AudioMixingRule.Builder excludeRule(android.media.AudioAttributes, int) throws java.lang.IllegalArgumentException; - } - - public class AudioPolicy { - method public int attachMixes(@NonNull java.util.List<android.media.audiopolicy.AudioMix>); - method public android.media.AudioRecord createAudioRecordSink(android.media.audiopolicy.AudioMix) throws java.lang.IllegalArgumentException; - method public android.media.AudioTrack createAudioTrackSource(android.media.audiopolicy.AudioMix) throws java.lang.IllegalArgumentException; - method public int detachMixes(@NonNull java.util.List<android.media.audiopolicy.AudioMix>); - method public int getFocusDuckingBehavior(); - method public int getStatus(); - method public boolean removeUidDeviceAffinity(int); - method public boolean removeUserIdDeviceAffinity(int); - method public int setFocusDuckingBehavior(int) throws java.lang.IllegalArgumentException, java.lang.IllegalStateException; - method public void setRegistration(String); - method public boolean setUidDeviceAffinity(int, @NonNull java.util.List<android.media.AudioDeviceInfo>); - method public boolean setUserIdDeviceAffinity(int, @NonNull java.util.List<android.media.AudioDeviceInfo>); - method public String toLogFriendlyString(); - field public static final int FOCUS_POLICY_DUCKING_DEFAULT = 0; // 0x0 - field public static final int FOCUS_POLICY_DUCKING_IN_APP = 0; // 0x0 - field public static final int FOCUS_POLICY_DUCKING_IN_POLICY = 1; // 0x1 - field public static final int POLICY_STATUS_REGISTERED = 2; // 0x2 - field public static final int POLICY_STATUS_UNREGISTERED = 1; // 0x1 - } - - public abstract static class AudioPolicy.AudioPolicyFocusListener { - ctor public AudioPolicy.AudioPolicyFocusListener(); - method public void onAudioFocusAbandon(android.media.AudioFocusInfo); - method public void onAudioFocusGrant(android.media.AudioFocusInfo, int); - method public void onAudioFocusLoss(android.media.AudioFocusInfo, boolean); - method public void onAudioFocusRequest(android.media.AudioFocusInfo, int); - } - - public abstract static class AudioPolicy.AudioPolicyStatusListener { - ctor public AudioPolicy.AudioPolicyStatusListener(); - method public void onMixStateUpdate(android.media.audiopolicy.AudioMix); - method public void onStatusChange(); - } - - public abstract static class AudioPolicy.AudioPolicyVolumeCallback { - ctor public AudioPolicy.AudioPolicyVolumeCallback(); - method public void onVolumeAdjustment(int); - } - public static class AudioPolicy.Builder { - ctor public AudioPolicy.Builder(android.content.Context); - method @NonNull public android.media.audiopolicy.AudioPolicy.Builder addMix(@NonNull android.media.audiopolicy.AudioMix) throws java.lang.IllegalArgumentException; - method @NonNull public android.media.audiopolicy.AudioPolicy build(); - method public void setAudioPolicyFocusListener(android.media.audiopolicy.AudioPolicy.AudioPolicyFocusListener); - method public void setAudioPolicyStatusListener(android.media.audiopolicy.AudioPolicy.AudioPolicyStatusListener); - method @NonNull public android.media.audiopolicy.AudioPolicy.Builder setAudioPolicyVolumeCallback(@NonNull android.media.audiopolicy.AudioPolicy.AudioPolicyVolumeCallback); - method @NonNull public android.media.audiopolicy.AudioPolicy.Builder setIsAudioFocusPolicy(boolean); method @NonNull public android.media.audiopolicy.AudioPolicy.Builder setIsTestFocusPolicy(boolean); - method @NonNull public android.media.audiopolicy.AudioPolicy.Builder setLooper(@NonNull android.os.Looper) throws java.lang.IllegalArgumentException; - } - -} - -package android.media.musicrecognition { - - public class MusicRecognitionManager { - field public static final int RECOGNITION_FAILED_AUDIO_UNAVAILABLE = 7; // 0x7 - field public static final int RECOGNITION_FAILED_NOT_FOUND = 1; // 0x1 - field public static final int RECOGNITION_FAILED_NO_CONNECTIVITY = 2; // 0x2 - field public static final int RECOGNITION_FAILED_SERVICE_KILLED = 5; // 0x5 - field public static final int RECOGNITION_FAILED_SERVICE_UNAVAILABLE = 3; // 0x3 - field public static final int RECOGNITION_FAILED_TIMEOUT = 6; // 0x6 - field public static final int RECOGNITION_FAILED_UNKNOWN = -1; // 0xffffffff - } - - public static interface MusicRecognitionManager.RecognitionCallback { - method public void onAudioStreamClosed(); - method public void onRecognitionFailed(@NonNull android.media.musicrecognition.RecognitionRequest, int); - method public void onRecognitionSucceeded(@NonNull android.media.musicrecognition.RecognitionRequest, @NonNull android.media.MediaMetadata, @Nullable android.os.Bundle); - } - - public abstract class MusicRecognitionService extends android.app.Service { - ctor public MusicRecognitionService(); - method public abstract void onRecognize(@NonNull android.os.ParcelFileDescriptor, @NonNull android.media.AudioFormat, @NonNull android.media.musicrecognition.MusicRecognitionService.Callback); - } - - public static interface MusicRecognitionService.Callback { - method public void onRecognitionFailed(int); - method public void onRecognitionSucceeded(@NonNull android.media.MediaMetadata, @Nullable android.os.Bundle); - } - - public final class RecognitionRequest implements android.os.Parcelable { - method public int describeContents(); - method @NonNull public android.media.AudioAttributes getAudioAttributes(); - method @NonNull public android.media.AudioFormat getAudioFormat(); - method public int getCaptureSession(); - method public int getIgnoreBeginningFrames(); - method public int getMaxAudioLengthSeconds(); - method public void writeToParcel(@NonNull android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.media.musicrecognition.RecognitionRequest> CREATOR; - } - - public static final class RecognitionRequest.Builder { - ctor public RecognitionRequest.Builder(); - method @NonNull public android.media.musicrecognition.RecognitionRequest build(); - method @NonNull public android.media.musicrecognition.RecognitionRequest.Builder setAudioAttributes(@NonNull android.media.AudioAttributes); - method @NonNull public android.media.musicrecognition.RecognitionRequest.Builder setAudioFormat(@NonNull android.media.AudioFormat); - method @NonNull public android.media.musicrecognition.RecognitionRequest.Builder setCaptureSession(int); - method @NonNull public android.media.musicrecognition.RecognitionRequest.Builder setIgnoreBeginningFrames(int); - method @NonNull public android.media.musicrecognition.RecognitionRequest.Builder setMaxAudioLengthSeconds(int); } } @@ -2135,242 +1053,33 @@ package android.media.tv.tuner { public final class TunerVersionChecker { method public static int getMajorVersion(int); method public static int getMinorVersion(int); - method public static int getTunerVersion(); method public static boolean isHigherOrEqualVersionTo(int); method public static boolean supportTunerVersion(int); - field public static final int TUNER_VERSION_1_0 = 65536; // 0x10000 - field public static final int TUNER_VERSION_1_1 = 65537; // 0x10001 - field public static final int TUNER_VERSION_UNKNOWN = 0; // 0x0 - } - -} - -package android.metrics { - - public class LogMaker { - ctor public LogMaker(int); - ctor public LogMaker(Object[]); - method public android.metrics.LogMaker addTaggedData(int, Object); - method public android.metrics.LogMaker clearCategory(); - method public android.metrics.LogMaker clearPackageName(); - method public android.metrics.LogMaker clearSubtype(); - method public android.metrics.LogMaker clearTaggedData(int); - method public android.metrics.LogMaker clearType(); - method public void deserialize(Object[]); - method public int getCategory(); - method public long getCounterBucket(); - method public String getCounterName(); - method public int getCounterValue(); - method public String getPackageName(); - method public int getProcessId(); - method public int getSubtype(); - method public Object getTaggedData(int); - method public long getTimestamp(); - method public int getType(); - method public int getUid(); - method public boolean isLongCounterBucket(); - method public boolean isSubsetOf(android.metrics.LogMaker); - method public boolean isValidValue(Object); - method public Object[] serialize(); - method public android.metrics.LogMaker setCategory(int); - method public android.metrics.LogMaker setPackageName(String); - method public android.metrics.LogMaker setSubtype(int); - method public android.metrics.LogMaker setType(int); - } - - public class MetricsReader { - ctor public MetricsReader(); - method public void checkpoint(); - method public boolean hasNext(); - method public android.metrics.LogMaker next(); - method public void read(long); - method public void reset(); } } package android.net { - public class CaptivePortal implements android.os.Parcelable { - method public void logEvent(int, @NonNull String); - method @RequiresPermission(android.Manifest.permission.NETWORK_STACK) public void reevaluateNetwork(); - method public void useNetwork(); - field public static final int APP_REQUEST_REEVALUATION_REQUIRED = 100; // 0x64 - field public static final int APP_RETURN_DISMISSED = 0; // 0x0 - field public static final int APP_RETURN_UNWANTED = 1; // 0x1 - field public static final int APP_RETURN_WANTED_AS_IS = 2; // 0x2 - } - - public final class CaptivePortalData implements android.os.Parcelable { - method public int describeContents(); - method public long getByteLimit(); - method public long getExpiryTimeMillis(); - method public long getRefreshTimeMillis(); - method @Nullable public android.net.Uri getUserPortalUrl(); - method @Nullable public android.net.Uri getVenueInfoUrl(); - method public boolean isCaptive(); - method public boolean isSessionExtendable(); - method public void writeToParcel(@NonNull android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.net.CaptivePortalData> CREATOR; - } - - public static class CaptivePortalData.Builder { - ctor public CaptivePortalData.Builder(); - ctor public CaptivePortalData.Builder(@Nullable android.net.CaptivePortalData); - method @NonNull public android.net.CaptivePortalData build(); - method @NonNull public android.net.CaptivePortalData.Builder setBytesRemaining(long); - method @NonNull public android.net.CaptivePortalData.Builder setCaptive(boolean); - method @NonNull public android.net.CaptivePortalData.Builder setExpiryTime(long); - method @NonNull public android.net.CaptivePortalData.Builder setRefreshTime(long); - method @NonNull public android.net.CaptivePortalData.Builder setSessionExtendable(boolean); - method @NonNull public android.net.CaptivePortalData.Builder setUserPortalUrl(@Nullable android.net.Uri); - method @NonNull public android.net.CaptivePortalData.Builder setVenueInfoUrl(@Nullable android.net.Uri); - } - public class ConnectivityManager { method @RequiresPermission(anyOf={"android.permission.MANAGE_TEST_NETWORKS", android.Manifest.permission.NETWORK_STACK}) public void simulateDataStall(int, long, @NonNull android.net.Network, @NonNull android.os.PersistableBundle); - method @RequiresPermission(android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) public void startCaptivePortalApp(@NonNull android.net.Network, @NonNull android.os.Bundle); - field public static final String EXTRA_CAPTIVE_PORTAL_PROBE_SPEC = "android.net.extra.CAPTIVE_PORTAL_PROBE_SPEC"; - field public static final String EXTRA_CAPTIVE_PORTAL_USER_AGENT = "android.net.extra.CAPTIVE_PORTAL_USER_AGENT"; } public class EthernetManager { - method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public android.net.EthernetManager.TetheredInterfaceRequest requestTetheredInterface(@NonNull java.util.concurrent.Executor, @NonNull android.net.EthernetManager.TetheredInterfaceCallback); method public void setIncludeTestInterfaces(boolean); } - public static interface EthernetManager.TetheredInterfaceCallback { - method public void onAvailable(@NonNull String); - method public void onUnavailable(); - } - - public static class EthernetManager.TetheredInterfaceRequest { - method public void release(); - } - - public final class IpPrefix implements android.os.Parcelable { - ctor public IpPrefix(@NonNull java.net.InetAddress, @IntRange(from=0, to=128) int); - ctor public IpPrefix(@NonNull String); - } - public final class IpSecManager { field public static final int INVALID_SECURITY_PARAMETER_INDEX = 0; // 0x0 } - public class LinkAddress implements android.os.Parcelable { - ctor public LinkAddress(@NonNull java.net.InetAddress, @IntRange(from=0, to=128) int, int, int); - ctor public LinkAddress(@NonNull java.net.InetAddress, @IntRange(from=0, to=128) int, int, int, long, long); - ctor public LinkAddress(@NonNull java.net.InetAddress, @IntRange(from=0, to=128) int); - ctor public LinkAddress(@NonNull String); - ctor public LinkAddress(@NonNull String, int, int); - method public long getDeprecationTime(); - method public long getExpirationTime(); - method public boolean isGlobalPreferred(); - method public boolean isIpv4(); - method public boolean isIpv6(); - method public boolean isSameAddressAs(@Nullable android.net.LinkAddress); - } - - public final class LinkProperties implements android.os.Parcelable { - ctor public LinkProperties(@Nullable android.net.LinkProperties); - ctor public LinkProperties(@Nullable android.net.LinkProperties, boolean); - method public boolean addDnsServer(@NonNull java.net.InetAddress); - method public boolean addLinkAddress(@NonNull android.net.LinkAddress); - method @Nullable public android.net.Uri getCaptivePortalApiUrl(); - method @Nullable public android.net.CaptivePortalData getCaptivePortalData(); - method @NonNull public java.util.List<java.net.InetAddress> getPcscfServers(); - method @Nullable public String getTcpBufferSizes(); - method @NonNull public java.util.List<java.net.InetAddress> getValidatedPrivateDnsServers(); - method public boolean hasGlobalIpv6Address(); - method public boolean hasIpv4Address(); - method public boolean hasIpv6DefaultRoute(); - method public boolean isIpv4Provisioned(); - method public boolean isIpv6Provisioned(); - method public boolean isProvisioned(); - method public boolean isReachable(@NonNull java.net.InetAddress); - method public boolean removeDnsServer(@NonNull java.net.InetAddress); - method public boolean removeLinkAddress(@NonNull android.net.LinkAddress); - method public boolean removeRoute(@NonNull android.net.RouteInfo); - method public void setCaptivePortalApiUrl(@Nullable android.net.Uri); - method public void setCaptivePortalData(@Nullable android.net.CaptivePortalData); - method public void setPcscfServers(@NonNull java.util.Collection<java.net.InetAddress>); - method public void setPrivateDnsServerName(@Nullable String); - method public void setTcpBufferSizes(@Nullable String); - method public void setUsePrivateDns(boolean); - method public void setValidatedPrivateDnsServers(@NonNull java.util.Collection<java.net.InetAddress>); - } - - public class Network implements android.os.Parcelable { - ctor public Network(@NonNull android.net.Network); - method public int getNetId(); - method @NonNull public android.net.Network getPrivateDnsBypassingCopy(); - } - public final class NetworkCapabilities implements android.os.Parcelable { - method @NonNull public int[] getAdministratorUids(); method public int[] getCapabilities(); - method @Nullable public String getSsid(); - method @NonNull public int[] getTransportTypes(); - method public boolean satisfiedByNetworkCapabilities(@Nullable android.net.NetworkCapabilities); field public static final int TRANSPORT_TEST = 7; // 0x7 } - public static final class NetworkCapabilities.Builder { - ctor public NetworkCapabilities.Builder(); - ctor public NetworkCapabilities.Builder(@NonNull android.net.NetworkCapabilities); - method @NonNull public android.net.NetworkCapabilities.Builder addCapability(int); - method @NonNull public android.net.NetworkCapabilities.Builder addTransportType(int); - method @NonNull public android.net.NetworkCapabilities build(); - method @NonNull public android.net.NetworkCapabilities.Builder removeCapability(int); - method @NonNull public android.net.NetworkCapabilities.Builder removeTransportType(int); - method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public android.net.NetworkCapabilities.Builder setAdministratorUids(@NonNull int[]); - method @NonNull public android.net.NetworkCapabilities.Builder setLinkDownstreamBandwidthKbps(int); - method @NonNull public android.net.NetworkCapabilities.Builder setLinkUpstreamBandwidthKbps(int); - method @NonNull public android.net.NetworkCapabilities.Builder setNetworkSpecifier(@Nullable android.net.NetworkSpecifier); - method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public android.net.NetworkCapabilities.Builder setOwnerUid(int); - method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public android.net.NetworkCapabilities.Builder setRequestorPackageName(@Nullable String); - method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public android.net.NetworkCapabilities.Builder setRequestorUid(int); - method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_SIGNAL_STRENGTH_WAKEUP) public android.net.NetworkCapabilities.Builder setSignalStrength(int); - method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public android.net.NetworkCapabilities.Builder setSsid(@Nullable String); - method @NonNull public android.net.NetworkCapabilities.Builder setTransportInfo(@Nullable android.net.TransportInfo); - } - public class NetworkStack { - method @Nullable public static android.os.IBinder getService(); method public static void setServiceForTest(@Nullable android.os.IBinder); - field public static final String PERMISSION_MAINLINE_NETWORK_STACK = "android.permission.MAINLINE_NETWORK_STACK"; - } - - public final class RouteInfo implements android.os.Parcelable { - ctor public RouteInfo(@Nullable android.net.IpPrefix, @Nullable java.net.InetAddress, @Nullable String, int); - method public int getType(); - field public static final int RTN_THROW = 9; // 0x9 - field public static final int RTN_UNICAST = 1; // 0x1 - field public static final int RTN_UNREACHABLE = 7; // 0x7 - } - - public final class StaticIpConfiguration implements android.os.Parcelable { - ctor public StaticIpConfiguration(); - ctor public StaticIpConfiguration(@Nullable android.net.StaticIpConfiguration); - method public void addDnsServer(@NonNull java.net.InetAddress); - method public void clear(); - method public int describeContents(); - method @NonNull public java.util.List<java.net.InetAddress> getDnsServers(); - method @Nullable public String getDomains(); - method @Nullable public java.net.InetAddress getGateway(); - method @Nullable public android.net.LinkAddress getIpAddress(); - method @NonNull public java.util.List<android.net.RouteInfo> getRoutes(@Nullable String); - method public void writeToParcel(android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.net.StaticIpConfiguration> CREATOR; - } - - public static final class StaticIpConfiguration.Builder { - ctor public StaticIpConfiguration.Builder(); - method @NonNull public android.net.StaticIpConfiguration build(); - method @NonNull public android.net.StaticIpConfiguration.Builder setDnsServers(@NonNull Iterable<java.net.InetAddress>); - method @NonNull public android.net.StaticIpConfiguration.Builder setDomains(@Nullable String); - method @NonNull public android.net.StaticIpConfiguration.Builder setGateway(@Nullable java.net.InetAddress); - method @NonNull public android.net.StaticIpConfiguration.Builder setIpAddress(@Nullable android.net.LinkAddress); } public final class TestNetworkInterface implements android.os.Parcelable { @@ -2389,100 +1098,6 @@ package android.net { method public void teardownTestNetwork(@NonNull android.net.Network); } - public final class TetheredClient implements android.os.Parcelable { - ctor public TetheredClient(@NonNull android.net.MacAddress, @NonNull java.util.Collection<android.net.TetheredClient.AddressInfo>, int); - method public int describeContents(); - method @NonNull public java.util.List<android.net.TetheredClient.AddressInfo> getAddresses(); - method @NonNull public android.net.MacAddress getMacAddress(); - method public int getTetheringType(); - method public void writeToParcel(@NonNull android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.net.TetheredClient> CREATOR; - } - - public static final class TetheredClient.AddressInfo implements android.os.Parcelable { - method public int describeContents(); - method @NonNull public android.net.LinkAddress getAddress(); - method @Nullable public String getHostname(); - method public void writeToParcel(@NonNull android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.net.TetheredClient.AddressInfo> CREATOR; - } - - public class TetheringManager { - method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public void registerTetheringEventCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.TetheringEventCallback); - method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public void requestLatestTetheringEntitlementResult(int, boolean, @NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.OnTetheringEntitlementResultListener); - method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public void startTethering(@NonNull android.net.TetheringManager.TetheringRequest, @NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.StartTetheringCallback); - method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public void stopAllTethering(); - method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public void stopTethering(int); - method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.ACCESS_NETWORK_STATE}) public void unregisterTetheringEventCallback(@NonNull android.net.TetheringManager.TetheringEventCallback); - field public static final String ACTION_TETHER_STATE_CHANGED = "android.net.conn.TETHER_STATE_CHANGED"; - field public static final String EXTRA_ACTIVE_LOCAL_ONLY = "android.net.extra.ACTIVE_LOCAL_ONLY"; - field public static final String EXTRA_ACTIVE_TETHER = "tetherArray"; - field public static final String EXTRA_AVAILABLE_TETHER = "availableArray"; - field public static final String EXTRA_ERRORED_TETHER = "erroredArray"; - field public static final int TETHERING_BLUETOOTH = 2; // 0x2 - field public static final int TETHERING_ETHERNET = 5; // 0x5 - field public static final int TETHERING_INVALID = -1; // 0xffffffff - field public static final int TETHERING_NCM = 4; // 0x4 - field public static final int TETHERING_USB = 1; // 0x1 - field public static final int TETHERING_WIFI = 0; // 0x0 - field public static final int TETHERING_WIFI_P2P = 3; // 0x3 - field public static final int TETHER_ERROR_DHCPSERVER_ERROR = 12; // 0xc - field public static final int TETHER_ERROR_DISABLE_FORWARDING_ERROR = 9; // 0x9 - field public static final int TETHER_ERROR_ENABLE_FORWARDING_ERROR = 8; // 0x8 - field public static final int TETHER_ERROR_ENTITLEMENT_UNKNOWN = 13; // 0xd - field public static final int TETHER_ERROR_IFACE_CFG_ERROR = 10; // 0xa - field public static final int TETHER_ERROR_INTERNAL_ERROR = 5; // 0x5 - field public static final int TETHER_ERROR_NO_ACCESS_TETHERING_PERMISSION = 15; // 0xf - field public static final int TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION = 14; // 0xe - field public static final int TETHER_ERROR_NO_ERROR = 0; // 0x0 - field public static final int TETHER_ERROR_PROVISIONING_FAILED = 11; // 0xb - field public static final int TETHER_ERROR_SERVICE_UNAVAIL = 2; // 0x2 - field public static final int TETHER_ERROR_TETHER_IFACE_ERROR = 6; // 0x6 - field public static final int TETHER_ERROR_UNAVAIL_IFACE = 4; // 0x4 - field public static final int TETHER_ERROR_UNKNOWN_IFACE = 1; // 0x1 - field public static final int TETHER_ERROR_UNKNOWN_TYPE = 16; // 0x10 - field public static final int TETHER_ERROR_UNSUPPORTED = 3; // 0x3 - field public static final int TETHER_ERROR_UNTETHER_IFACE_ERROR = 7; // 0x7 - field public static final int TETHER_HARDWARE_OFFLOAD_FAILED = 2; // 0x2 - field public static final int TETHER_HARDWARE_OFFLOAD_STARTED = 1; // 0x1 - field public static final int TETHER_HARDWARE_OFFLOAD_STOPPED = 0; // 0x0 - } - - public static interface TetheringManager.OnTetheringEntitlementResultListener { - method public void onTetheringEntitlementResult(int); - } - - public static interface TetheringManager.StartTetheringCallback { - method public default void onTetheringFailed(int); - method public default void onTetheringStarted(); - } - - public static interface TetheringManager.TetheringEventCallback { - method public default void onClientsChanged(@NonNull java.util.Collection<android.net.TetheredClient>); - method public default void onError(@NonNull String, int); - method public default void onOffloadStatusChanged(int); - method public default void onTetherableInterfacesChanged(@NonNull java.util.List<java.lang.String>); - method public default void onTetheredInterfacesChanged(@NonNull java.util.List<java.lang.String>); - method public default void onTetheringSupported(boolean); - method public default void onUpstreamChanged(@Nullable android.net.Network); - } - - public static class TetheringManager.TetheringRequest { - method @Nullable public android.net.LinkAddress getClientStaticIpv4Address(); - method @Nullable public android.net.LinkAddress getLocalIpv4Address(); - method public boolean getShouldShowEntitlementUi(); - method public int getTetheringType(); - method public boolean isExemptFromEntitlementCheck(); - } - - public static class TetheringManager.TetheringRequest.Builder { - ctor public TetheringManager.TetheringRequest.Builder(int); - method @NonNull public android.net.TetheringManager.TetheringRequest build(); - method @NonNull @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public android.net.TetheringManager.TetheringRequest.Builder setExemptFromEntitlementCheck(boolean); - method @NonNull @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public android.net.TetheringManager.TetheringRequest.Builder setShouldShowEntitlementUi(boolean); - method @NonNull @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public android.net.TetheringManager.TetheringRequest.Builder setStaticIpv4Addresses(@NonNull android.net.LinkAddress, @NonNull android.net.LinkAddress); - } - public class TrafficStats { method public static long getLoopbackRxBytes(); method public static long getLoopbackRxPackets(); @@ -2492,228 +1107,8 @@ package android.net { } -package android.net.apf { - - public final class ApfCapabilities implements android.os.Parcelable { - ctor public ApfCapabilities(int, int, int); - method public int describeContents(); - method public static boolean getApfDrop8023Frames(); - method @NonNull public static int[] getApfEtherTypeBlackList(); - method public boolean hasDataAccess(); - method public void writeToParcel(android.os.Parcel, int); - field public static final android.os.Parcelable.Creator<android.net.apf.ApfCapabilities> CREATOR; - field public final int apfPacketFormat; - field public final int apfVersionSupported; - field public final int maximumApfProgramSize; - } - -} - -package android.net.metrics { - - public final class ApfProgramEvent implements android.net.metrics.IpConnectivityLog.Event { - } - - public static final class ApfProgramEvent.Builder { - ctor public ApfProgramEvent.Builder(); - method @NonNull public android.net.metrics.ApfProgramEvent build(); - method @NonNull public android.net.metrics.ApfProgramEvent.Builder setActualLifetime(long); - method @NonNull public android.net.metrics.ApfProgramEvent.Builder setCurrentRas(int); - method @NonNull public android.net.metrics.ApfProgramEvent.Builder setFilteredRas(int); - method @NonNull public android.net.metrics.ApfProgramEvent.Builder setFlags(boolean, boolean); - method @NonNull public android.net.metrics.ApfProgramEvent.Builder setLifetime(long); - method @NonNull public android.net.metrics.ApfProgramEvent.Builder setProgramLength(int); - } - - public final class ApfStats implements android.net.metrics.IpConnectivityLog.Event { - } - - public static final class ApfStats.Builder { - ctor public ApfStats.Builder(); - method @NonNull public android.net.metrics.ApfStats build(); - method @NonNull public android.net.metrics.ApfStats.Builder setDroppedRas(int); - method @NonNull public android.net.metrics.ApfStats.Builder setDurationMs(long); - method @NonNull public android.net.metrics.ApfStats.Builder setMatchingRas(int); - method @NonNull public android.net.metrics.ApfStats.Builder setMaxProgramSize(int); - method @NonNull public android.net.metrics.ApfStats.Builder setParseErrors(int); - method @NonNull public android.net.metrics.ApfStats.Builder setProgramUpdates(int); - method @NonNull public android.net.metrics.ApfStats.Builder setProgramUpdatesAll(int); - method @NonNull public android.net.metrics.ApfStats.Builder setProgramUpdatesAllowingMulticast(int); - method @NonNull public android.net.metrics.ApfStats.Builder setReceivedRas(int); - method @NonNull public android.net.metrics.ApfStats.Builder setZeroLifetimeRas(int); - } - - public final class DhcpClientEvent implements android.net.metrics.IpConnectivityLog.Event { - } - - public static final class DhcpClientEvent.Builder { - ctor public DhcpClientEvent.Builder(); - method @NonNull public android.net.metrics.DhcpClientEvent build(); - method @NonNull public android.net.metrics.DhcpClientEvent.Builder setDurationMs(int); - method @NonNull public android.net.metrics.DhcpClientEvent.Builder setMsg(String); - } - - public final class DhcpErrorEvent implements android.net.metrics.IpConnectivityLog.Event { - ctor public DhcpErrorEvent(int); - method public static int errorCodeWithOption(int, int); - field public static final int BOOTP_TOO_SHORT = 67174400; // 0x4010000 - field public static final int BUFFER_UNDERFLOW = 83951616; // 0x5010000 - field public static final int DHCP_BAD_MAGIC_COOKIE = 67239936; // 0x4020000 - field public static final int DHCP_ERROR = 4; // 0x4 - field public static final int DHCP_INVALID_OPTION_LENGTH = 67305472; // 0x4030000 - field public static final int DHCP_NO_COOKIE = 67502080; // 0x4060000 - field public static final int DHCP_NO_MSG_TYPE = 67371008; // 0x4040000 - field public static final int DHCP_UNKNOWN_MSG_TYPE = 67436544; // 0x4050000 - field public static final int L2_ERROR = 1; // 0x1 - field public static final int L2_TOO_SHORT = 16842752; // 0x1010000 - field public static final int L2_WRONG_ETH_TYPE = 16908288; // 0x1020000 - field public static final int L3_ERROR = 2; // 0x2 - field public static final int L3_INVALID_IP = 33751040; // 0x2030000 - field public static final int L3_NOT_IPV4 = 33685504; // 0x2020000 - field public static final int L3_TOO_SHORT = 33619968; // 0x2010000 - field public static final int L4_ERROR = 3; // 0x3 - field public static final int L4_NOT_UDP = 50397184; // 0x3010000 - field public static final int L4_WRONG_PORT = 50462720; // 0x3020000 - field public static final int MISC_ERROR = 5; // 0x5 - field public static final int PARSING_ERROR = 84082688; // 0x5030000 - field public static final int RECEIVE_ERROR = 84017152; // 0x5020000 - } - - public class IpConnectivityLog { - ctor public IpConnectivityLog(); - method public boolean log(long, @NonNull android.net.metrics.IpConnectivityLog.Event); - method public boolean log(@NonNull String, @NonNull android.net.metrics.IpConnectivityLog.Event); - method public boolean log(@NonNull android.net.Network, @NonNull int[], @NonNull android.net.metrics.IpConnectivityLog.Event); - method public boolean log(int, @NonNull int[], @NonNull android.net.metrics.IpConnectivityLog.Event); - method public boolean log(@NonNull android.net.metrics.IpConnectivityLog.Event); - } - - public static interface IpConnectivityLog.Event extends android.os.Parcelable { - } - - public final class IpManagerEvent implements android.net.metrics.IpConnectivityLog.Event { - ctor public IpManagerEvent(int, long); - field public static final int COMPLETE_LIFECYCLE = 3; // 0x3 - field public static final int ERROR_INTERFACE_NOT_FOUND = 8; // 0x8 - field public static final int ERROR_INVALID_PROVISIONING = 7; // 0x7 - field public static final int ERROR_STARTING_IPREACHABILITYMONITOR = 6; // 0x6 - field public static final int ERROR_STARTING_IPV4 = 4; // 0x4 - field public static final int ERROR_STARTING_IPV6 = 5; // 0x5 - field public static final int PROVISIONING_FAIL = 2; // 0x2 - field public static final int PROVISIONING_OK = 1; // 0x1 - } - - public final class IpReachabilityEvent implements android.net.metrics.IpConnectivityLog.Event { - ctor public IpReachabilityEvent(int); - field public static final int NUD_FAILED = 512; // 0x200 - field public static final int NUD_FAILED_ORGANIC = 1024; // 0x400 - field public static final int PROBE = 256; // 0x100 - field public static final int PROVISIONING_LOST = 768; // 0x300 - field public static final int PROVISIONING_LOST_ORGANIC = 1280; // 0x500 - } - - public final class NetworkEvent implements android.net.metrics.IpConnectivityLog.Event { - ctor public NetworkEvent(int, long); - ctor public NetworkEvent(int); - field public static final int NETWORK_CAPTIVE_PORTAL_FOUND = 4; // 0x4 - field public static final int NETWORK_CONNECTED = 1; // 0x1 - field public static final int NETWORK_CONSECUTIVE_DNS_TIMEOUT_FOUND = 12; // 0xc - field public static final int NETWORK_DISCONNECTED = 7; // 0x7 - field public static final int NETWORK_FIRST_VALIDATION_PORTAL_FOUND = 10; // 0xa - field public static final int NETWORK_FIRST_VALIDATION_SUCCESS = 8; // 0x8 - field public static final int NETWORK_LINGER = 5; // 0x5 - field public static final int NETWORK_PARTIAL_CONNECTIVITY = 13; // 0xd - field public static final int NETWORK_REVALIDATION_PORTAL_FOUND = 11; // 0xb - field public static final int NETWORK_REVALIDATION_SUCCESS = 9; // 0x9 - field public static final int NETWORK_UNLINGER = 6; // 0x6 - field public static final int NETWORK_VALIDATED = 2; // 0x2 - field public static final int NETWORK_VALIDATION_FAILED = 3; // 0x3 - } - - public final class RaEvent implements android.net.metrics.IpConnectivityLog.Event { - } - - public static final class RaEvent.Builder { - ctor public RaEvent.Builder(); - method @NonNull public android.net.metrics.RaEvent build(); - method @NonNull public android.net.metrics.RaEvent.Builder updateDnsslLifetime(long); - method @NonNull public android.net.metrics.RaEvent.Builder updatePrefixPreferredLifetime(long); - method @NonNull public android.net.metrics.RaEvent.Builder updatePrefixValidLifetime(long); - method @NonNull public android.net.metrics.RaEvent.Builder updateRdnssLifetime(long); - method @NonNull public android.net.metrics.RaEvent.Builder updateRouteInfoLifetime(long); - method @NonNull public android.net.metrics.RaEvent.Builder updateRouterLifetime(long); - } - - public final class ValidationProbeEvent implements android.net.metrics.IpConnectivityLog.Event { - method @NonNull public static String getProbeName(int); - field public static final int DNS_FAILURE = 0; // 0x0 - field public static final int DNS_SUCCESS = 1; // 0x1 - field public static final int PROBE_DNS = 0; // 0x0 - field public static final int PROBE_FALLBACK = 4; // 0x4 - field public static final int PROBE_HTTP = 1; // 0x1 - field public static final int PROBE_HTTPS = 2; // 0x2 - field public static final int PROBE_PAC = 3; // 0x3 - field public static final int PROBE_PRIVDNS = 5; // 0x5 - } - - public static final class ValidationProbeEvent.Builder { - ctor public ValidationProbeEvent.Builder(); - method @NonNull public android.net.metrics.ValidationProbeEvent build(); - method @NonNull public android.net.metrics.ValidationProbeEvent.Builder setDurationMs(long); - method @NonNull public android.net.metrics.ValidationProbeEvent.Builder setProbeType(int, boolean); - method @NonNull public android.net.metrics.ValidationProbeEvent.Builder setReturnCode(int); - } - -} - -package android.net.util { - - public final class SocketUtils { - method public static void bindSocketToInterface(@NonNull java.io.FileDescriptor, @NonNull String) throws android.system.ErrnoException; - method public static void closeSocket(@Nullable java.io.FileDescriptor) throws java.io.IOException; - method @NonNull public static java.net.SocketAddress makeNetlinkSocketAddress(int, int); - method @NonNull public static java.net.SocketAddress makePacketSocketAddress(int, int); - method @Deprecated @NonNull public static java.net.SocketAddress makePacketSocketAddress(int, @NonNull byte[]); - method @NonNull public static java.net.SocketAddress makePacketSocketAddress(int, int, @NonNull byte[]); - } - -} - package android.os { - public class BatteryManager { - method @RequiresPermission(android.Manifest.permission.POWER_SAVER) public boolean setChargingStateUpdateDelayMillis(int); - } - - public final class BugreportManager { - method @RequiresPermission(android.Manifest.permission.DUMP) public void cancelBugreport(); - method @RequiresPermission(android.Manifest.permission.DUMP) public void requestBugreport(@NonNull android.os.BugreportParams, @Nullable CharSequence, @Nullable CharSequence); - method @RequiresPermission(android.Manifest.permission.DUMP) public void startBugreport(@NonNull android.os.ParcelFileDescriptor, @Nullable android.os.ParcelFileDescriptor, @NonNull android.os.BugreportParams, @NonNull java.util.concurrent.Executor, @NonNull android.os.BugreportManager.BugreportCallback); - } - - public abstract static class BugreportManager.BugreportCallback { - ctor public BugreportManager.BugreportCallback(); - method public void onError(int); - method public void onFinished(); - method public void onProgress(@FloatRange(from=0.0f, to=100.0f) float); - field public static final int BUGREPORT_ERROR_ANOTHER_REPORT_IN_PROGRESS = 5; // 0x5 - field public static final int BUGREPORT_ERROR_INVALID_INPUT = 1; // 0x1 - field public static final int BUGREPORT_ERROR_RUNTIME = 2; // 0x2 - field public static final int BUGREPORT_ERROR_USER_CONSENT_TIMED_OUT = 4; // 0x4 - field public static final int BUGREPORT_ERROR_USER_DENIED_CONSENT = 3; // 0x3 - } - - public final class BugreportParams { - ctor public BugreportParams(int); - method public int getMode(); - field public static final int BUGREPORT_MODE_FULL = 0; // 0x0 - field public static final int BUGREPORT_MODE_INTERACTIVE = 1; // 0x1 - field public static final int BUGREPORT_MODE_REMOTE = 2; // 0x2 - field public static final int BUGREPORT_MODE_TELEPHONY = 4; // 0x4 - field public static final int BUGREPORT_MODE_WEAR = 3; // 0x3 - field public static final int BUGREPORT_MODE_WIFI = 5; // 0x5 - } - public class Build { method public static boolean is64BitAbi(String); field public static final boolean IS_EMULATOR; @@ -2726,18 +1121,12 @@ package android.os { } public class DeviceIdleManager { - method @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public void endIdle(@NonNull String); method @NonNull public String[] getSystemPowerWhitelist(); method @NonNull public String[] getSystemPowerWhitelistExceptIdle(); } public class Environment { method public static java.io.File buildPath(java.io.File, java.lang.String...); - method @NonNull public static java.io.File getOdmDirectory(); - method @NonNull public static java.io.File getOemDirectory(); - method @NonNull public static java.io.File getProductDirectory(); - method @NonNull public static java.io.File getSystemExtDirectory(); - method @NonNull public static java.io.File getVendorDirectory(); } public final class FileUtils { @@ -2746,228 +1135,11 @@ package android.os { method @NonNull public static byte[] digest(@NonNull java.io.InputStream, @NonNull String) throws java.io.IOException, java.security.NoSuchAlgorithmException; } - public class HidlMemory implements java.io.Closeable { - ctor public HidlMemory(@NonNull String, @IntRange(from=0) long, @Nullable android.os.NativeHandle); - method public void close() throws java.io.IOException; - method @NonNull public android.os.HidlMemory dup() throws java.io.IOException; - method protected void finalize(); - method @Nullable public android.os.NativeHandle getHandle(); - method @NonNull public String getName(); - method public long getSize(); - method @Nullable public android.os.NativeHandle releaseHandle(); - } - - public abstract class HwBinder implements android.os.IHwBinder { - ctor public HwBinder(); - method public static final void configureRpcThreadpool(long, boolean); - method public static void enableInstrumentation(); - method public static final android.os.IHwBinder getService(String, String) throws java.util.NoSuchElementException, android.os.RemoteException; - method public static final android.os.IHwBinder getService(String, String, boolean) throws java.util.NoSuchElementException, android.os.RemoteException; - method public static final void joinRpcThreadpool(); - method public abstract void onTransact(int, android.os.HwParcel, android.os.HwParcel, int) throws android.os.RemoteException; - method public final void registerService(String) throws android.os.RemoteException; - method public final void transact(int, android.os.HwParcel, android.os.HwParcel, int) throws android.os.RemoteException; - } - - public class HwBlob { - ctor public HwBlob(int); - method public final void copyToBoolArray(long, boolean[], int); - method public final void copyToDoubleArray(long, double[], int); - method public final void copyToFloatArray(long, float[], int); - method public final void copyToInt16Array(long, short[], int); - method public final void copyToInt32Array(long, int[], int); - method public final void copyToInt64Array(long, long[], int); - method public final void copyToInt8Array(long, byte[], int); - method public final boolean getBool(long); - method public final double getDouble(long); - method public final long getFieldHandle(long); - method public final float getFloat(long); - method public final short getInt16(long); - method public final int getInt32(long); - method public final long getInt64(long); - method public final byte getInt8(long); - method public final String getString(long); - method public final long handle(); - method public final void putBlob(long, android.os.HwBlob); - method public final void putBool(long, boolean); - method public final void putBoolArray(long, boolean[]); - method public final void putDouble(long, double); - method public final void putDoubleArray(long, double[]); - method public final void putFloat(long, float); - method public final void putFloatArray(long, float[]); - method public final void putHidlMemory(long, @NonNull android.os.HidlMemory); - method public final void putInt16(long, short); - method public final void putInt16Array(long, short[]); - method public final void putInt32(long, int); - method public final void putInt32Array(long, int[]); - method public final void putInt64(long, long); - method public final void putInt64Array(long, long[]); - method public final void putInt8(long, byte); - method public final void putInt8Array(long, byte[]); - method public final void putNativeHandle(long, @Nullable android.os.NativeHandle); - method public final void putString(long, String); - method public static Boolean[] wrapArray(@NonNull boolean[]); - method public static Long[] wrapArray(@NonNull long[]); - method public static Byte[] wrapArray(@NonNull byte[]); - method public static Short[] wrapArray(@NonNull short[]); - method public static Integer[] wrapArray(@NonNull int[]); - method public static Float[] wrapArray(@NonNull float[]); - method public static Double[] wrapArray(@NonNull double[]); - } - - public class HwParcel { - ctor public HwParcel(); - method public final void enforceInterface(String); - method public final boolean readBool(); - method public final java.util.ArrayList<java.lang.Boolean> readBoolVector(); - method public final android.os.HwBlob readBuffer(long); - method public final double readDouble(); - method public final java.util.ArrayList<java.lang.Double> readDoubleVector(); - method public final android.os.HwBlob readEmbeddedBuffer(long, long, long, boolean); - method @NonNull @Nullable public final android.os.HidlMemory readEmbeddedHidlMemory(long, long, long); - method @Nullable public final android.os.NativeHandle readEmbeddedNativeHandle(long, long); - method public final float readFloat(); - method public final java.util.ArrayList<java.lang.Float> readFloatVector(); - method @NonNull public final android.os.HidlMemory readHidlMemory(); - method public final short readInt16(); - method public final java.util.ArrayList<java.lang.Short> readInt16Vector(); - method public final int readInt32(); - method public final java.util.ArrayList<java.lang.Integer> readInt32Vector(); - method public final long readInt64(); - method public final java.util.ArrayList<java.lang.Long> readInt64Vector(); - method public final byte readInt8(); - method public final java.util.ArrayList<java.lang.Byte> readInt8Vector(); - method @Nullable public final android.os.NativeHandle readNativeHandle(); - method @NonNull public final java.util.ArrayList<android.os.NativeHandle> readNativeHandleVector(); - method public final String readString(); - method public final java.util.ArrayList<java.lang.String> readStringVector(); - method public final android.os.IHwBinder readStrongBinder(); - method public final void release(); - method public final void releaseTemporaryStorage(); - method public final void send(); - method public final void verifySuccess(); - method public final void writeBool(boolean); - method public final void writeBoolVector(java.util.ArrayList<java.lang.Boolean>); - method public final void writeBuffer(android.os.HwBlob); - method public final void writeDouble(double); - method public final void writeDoubleVector(java.util.ArrayList<java.lang.Double>); - method public final void writeFloat(float); - method public final void writeFloatVector(java.util.ArrayList<java.lang.Float>); - method public final void writeHidlMemory(@NonNull android.os.HidlMemory); - method public final void writeInt16(short); - method public final void writeInt16Vector(java.util.ArrayList<java.lang.Short>); - method public final void writeInt32(int); - method public final void writeInt32Vector(java.util.ArrayList<java.lang.Integer>); - method public final void writeInt64(long); - method public final void writeInt64Vector(java.util.ArrayList<java.lang.Long>); - method public final void writeInt8(byte); - method public final void writeInt8Vector(java.util.ArrayList<java.lang.Byte>); - method public final void writeInterfaceToken(String); - method public final void writeNativeHandle(@Nullable android.os.NativeHandle); - method public final void writeNativeHandleVector(@NonNull java.util.ArrayList<android.os.NativeHandle>); - method public final void writeStatus(int); - method public final void writeString(String); - method public final void writeStringVector(java.util.ArrayList<java.lang.String>); - method public final void writeStrongBinder(android.os.IHwBinder); - field public static final int STATUS_SUCCESS = 0; // 0x0 - } - - public interface IHwBinder { - method public boolean linkToDeath(android.os.IHwBinder.DeathRecipient, long); - method public android.os.IHwInterface queryLocalInterface(String); - method public void transact(int, android.os.HwParcel, android.os.HwParcel, int) throws android.os.RemoteException; - method public boolean unlinkToDeath(android.os.IHwBinder.DeathRecipient); - } - - public static interface IHwBinder.DeathRecipient { - method public void serviceDied(long); - } - - public interface IHwInterface { - method public android.os.IHwBinder asBinder(); - } - - public class IncidentManager { - method @RequiresPermission(android.Manifest.permission.APPROVE_INCIDENT_REPORTS) public void approveReport(android.net.Uri); - method @RequiresPermission("android.permission.REQUEST_INCIDENT_REPORT_APPROVAL") public void cancelAuthorization(android.os.IncidentManager.AuthListener); - method @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public void deleteIncidentReports(android.net.Uri); - method @RequiresPermission(android.Manifest.permission.APPROVE_INCIDENT_REPORTS) public void denyReport(android.net.Uri); - method @Nullable @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public android.os.IncidentManager.IncidentReport getIncidentReport(android.net.Uri); - method @NonNull @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public java.util.List<android.net.Uri> getIncidentReportList(String); - method @RequiresPermission(android.Manifest.permission.APPROVE_INCIDENT_REPORTS) public java.util.List<android.os.IncidentManager.PendingReport> getPendingReports(); - method public void registerSection(int, @NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.os.IncidentManager.DumpCallback); - method @RequiresPermission(allOf={android.Manifest.permission.DUMP, android.Manifest.permission.PACKAGE_USAGE_STATS}) public void reportIncident(android.os.IncidentReportArgs); - method @RequiresPermission("android.permission.REQUEST_INCIDENT_REPORT_APPROVAL") public void requestAuthorization(int, String, int, android.os.IncidentManager.AuthListener); - method public void unregisterSection(int); - field public static final int FLAG_CONFIRMATION_DIALOG = 1; // 0x1 - field public static final int PRIVACY_POLICY_AUTO = 200; // 0xc8 - field public static final int PRIVACY_POLICY_EXPLICIT = 100; // 0x64 - field public static final int PRIVACY_POLICY_LOCAL = 0; // 0x0 - } - - public static class IncidentManager.AuthListener { - ctor public IncidentManager.AuthListener(); - method public void onReportApproved(); - method public void onReportDenied(); - } - - public static class IncidentManager.DumpCallback { - ctor public IncidentManager.DumpCallback(); - method public void onDumpSection(int, @NonNull java.io.OutputStream); - } - - public static class IncidentManager.IncidentReport implements java.io.Closeable android.os.Parcelable { - ctor public IncidentManager.IncidentReport(android.os.Parcel); - method public void close(); - method public int describeContents(); - method public java.io.InputStream getInputStream() throws java.io.IOException; - method public long getPrivacyPolicy(); - method public long getTimestamp(); - method public void writeToParcel(android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.os.IncidentManager.IncidentReport> CREATOR; - } - - public static class IncidentManager.PendingReport { - ctor public IncidentManager.PendingReport(@NonNull android.net.Uri); - method public int getFlags(); - method @NonNull public String getRequestingPackage(); - method public long getTimestamp(); - method @NonNull public android.net.Uri getUri(); - } - - public final class IncidentReportArgs implements android.os.Parcelable { - ctor public IncidentReportArgs(); - ctor public IncidentReportArgs(android.os.Parcel); - method public void addHeader(byte[]); - method public void addSection(int); - method public boolean containsSection(int); - method public int describeContents(); - method public boolean isAll(); - method public void readFromParcel(android.os.Parcel); - method public int sectionCount(); - method public void setAll(boolean); - method public void setPrivacyPolicy(int); - method public void writeToParcel(android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.os.IncidentReportArgs> CREATOR; - } - public final class MessageQueue { method public int postSyncBarrier(); method public void removeSyncBarrier(int); } - public final class NativeHandle implements java.io.Closeable { - ctor public NativeHandle(); - ctor public NativeHandle(@NonNull java.io.FileDescriptor, boolean); - ctor public NativeHandle(@NonNull java.io.FileDescriptor[], @NonNull int[], boolean); - method public void close() throws java.io.IOException; - method @NonNull public android.os.NativeHandle dup() throws java.io.IOException; - method @NonNull public java.io.FileDescriptor getFileDescriptor(); - method @NonNull public java.io.FileDescriptor[] getFileDescriptors(); - method @NonNull public int[] getInts(); - method public boolean hasSingleFileDescriptor(); - } - public final class Parcel { method public boolean allowSquashing(); method public int readExceptionCode(); @@ -2979,24 +1151,7 @@ package android.os { } public final class PowerManager { - method @RequiresPermission(android.Manifest.permission.POWER_SAVER) public int getPowerSaveModeTrigger(); - method @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public void setBatteryDischargePrediction(@NonNull java.time.Duration, boolean); - method @RequiresPermission(android.Manifest.permission.POWER_SAVER) public boolean setDynamicPowerSaveHint(boolean, int); - method @RequiresPermission(anyOf={android.Manifest.permission.DEVICE_POWER, android.Manifest.permission.POWER_SAVER}) public boolean setPowerSaveModeEnabled(boolean); field public static final String ACTION_ENHANCED_DISCHARGE_PREDICTION_CHANGED = "android.os.action.ENHANCED_DISCHARGE_PREDICTION_CHANGED"; - field public static final int POWER_SAVE_MODE_TRIGGER_DYNAMIC = 1; // 0x1 - field public static final int POWER_SAVE_MODE_TRIGGER_PERCENTAGE = 0; // 0x0 - } - - public class PowerWhitelistManager { - method @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public void addToWhitelist(@NonNull String); - method @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public void addToWhitelist(@NonNull java.util.List<java.lang.String>); - method @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public void removeFromWhitelist(@NonNull String); - method @RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST) public void whitelistAppTemporarily(@NonNull String, long); - method @RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST) public long whitelistAppTemporarilyForEvent(@NonNull String, int, @NonNull String); - field public static final int EVENT_MMS = 2; // 0x2 - field public static final int EVENT_SMS = 1; // 0x1 - field public static final int EVENT_UNSPECIFIED = 0; // 0x0 } public class Process { @@ -3008,19 +1163,6 @@ package android.os { field public static final int NUM_UIDS_PER_APP_ZYGOTE = 100; // 0x64 } - public final class RemoteCallback implements android.os.Parcelable { - ctor public RemoteCallback(android.os.RemoteCallback.OnResultListener); - ctor public RemoteCallback(@NonNull android.os.RemoteCallback.OnResultListener, @Nullable android.os.Handler); - method public int describeContents(); - method public void sendResult(@Nullable android.os.Bundle); - method public void writeToParcel(android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.os.RemoteCallback> CREATOR; - } - - public static interface RemoteCallback.OnResultListener { - method public void onResult(@Nullable android.os.Bundle); - } - public final class StrictMode { method public static void conditionallyCheckInstanceCounts(); method public static void setViolationLogger(android.os.StrictMode.ViolationLogger); @@ -3058,31 +1200,11 @@ package android.os { method @NonNull public android.os.StrictMode.VmPolicy.Builder permitIncorrectContextUse(); } - public class SystemConfigManager { - method @NonNull @RequiresPermission(android.Manifest.permission.READ_CARRIER_APP_INFO) public java.util.Set<java.lang.String> getDisabledUntilUsedPreinstalledCarrierApps(); - method @NonNull @RequiresPermission(android.Manifest.permission.READ_CARRIER_APP_INFO) public java.util.Map<java.lang.String,java.util.List<java.lang.String>> getDisabledUntilUsedPreinstalledCarrierAssociatedApps(); - } - - public class SystemProperties { - method @NonNull public static String get(@NonNull String); - method @NonNull public static String get(@NonNull String, @Nullable String); - method public static boolean getBoolean(@NonNull String, boolean); - method public static int getInt(@NonNull String, int); - method public static long getLong(@NonNull String, long); - } - public final class UserHandle implements android.os.Parcelable { - method public static int getAppId(int); - method public int getIdentifier(); method public static int getUid(int, int); method public static int getUserId(int); method public static boolean isApp(int); - method public static int myUserId(); - method public static android.os.UserHandle of(int); - field @NonNull public static final android.os.UserHandle ALL; - field @NonNull public static final android.os.UserHandle CURRENT; field public static final int MIN_SECONDARY_USER_ID = 10; // 0xa - field @NonNull public static final android.os.UserHandle SYSTEM; field public static final int USER_ALL = -1; // 0xffffffff field public static final int USER_NULL = -10000; // 0xffffd8f0 field public static final int USER_SYSTEM = 0; // 0x0 @@ -3091,7 +1213,6 @@ package android.os { public class UserManager { method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public boolean hasBaseUserRestriction(@NonNull String, @NonNull android.os.UserHandle); method public static boolean isSplitSystemUser(); - field public static final String ACTION_USER_RESTRICTIONS_CHANGED = "android.os.action.USER_RESTRICTIONS_CHANGED"; } public final class VibrationAttributes implements android.os.Parcelable { @@ -3148,17 +1269,6 @@ package android.os { field @NonNull public static final android.os.Parcelable.Creator<android.os.VibrationEffect.Waveform> CREATOR; } - public abstract class Vibrator { - method @RequiresPermission(android.Manifest.permission.ACCESS_VIBRATOR_STATE) public void addVibratorStateListener(@NonNull android.os.Vibrator.OnVibratorStateChangedListener); - method @RequiresPermission(android.Manifest.permission.ACCESS_VIBRATOR_STATE) public void addVibratorStateListener(@NonNull java.util.concurrent.Executor, @NonNull android.os.Vibrator.OnVibratorStateChangedListener); - method @RequiresPermission(android.Manifest.permission.ACCESS_VIBRATOR_STATE) public boolean isVibrating(); - method @RequiresPermission(android.Manifest.permission.ACCESS_VIBRATOR_STATE) public void removeVibratorStateListener(@NonNull android.os.Vibrator.OnVibratorStateChangedListener); - } - - public static interface Vibrator.OnVibratorStateChangedListener { - method public void onVibratorStateChanged(boolean); - } - public class VintfObject { method public static String[] getHalNamesAndVersions(); method public static String getSepolicyVersion(); @@ -3178,16 +1288,10 @@ package android.os { } public class WorkSource implements android.os.Parcelable { - ctor public WorkSource(int); method public boolean add(int); method public boolean add(int, String); method @Deprecated public android.os.WorkSource addReturningNewbs(android.os.WorkSource); - method @Nullable public String getPackageName(int); - method public int getUid(int); - method public boolean isEmpty(); method @Deprecated public android.os.WorkSource[] setReturningDiffs(android.os.WorkSource); - method public int size(); - method @NonNull public android.os.WorkSource withoutNames(); } } @@ -3247,36 +1351,6 @@ package android.os.health { } -package android.os.image { - - public class DynamicSystemClient { - ctor public DynamicSystemClient(@NonNull android.content.Context); - method @RequiresPermission(android.Manifest.permission.INSTALL_DYNAMIC_SYSTEM) public void bind(); - method public void setOnStatusChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.os.image.DynamicSystemClient.OnStatusChangedListener); - method public void setOnStatusChangedListener(@NonNull android.os.image.DynamicSystemClient.OnStatusChangedListener); - method @RequiresPermission(android.Manifest.permission.INSTALL_DYNAMIC_SYSTEM) public void start(@NonNull android.net.Uri, long); - method @RequiresPermission(android.Manifest.permission.INSTALL_DYNAMIC_SYSTEM) public void start(@NonNull android.net.Uri, long, long); - method @RequiresPermission(android.Manifest.permission.INSTALL_DYNAMIC_SYSTEM) public void unbind(); - field public static final int CAUSE_ERROR_EXCEPTION = 6; // 0x6 - field public static final int CAUSE_ERROR_INVALID_URL = 4; // 0x4 - field public static final int CAUSE_ERROR_IO = 3; // 0x3 - field public static final int CAUSE_ERROR_IPC = 5; // 0x5 - field public static final int CAUSE_INSTALL_CANCELLED = 2; // 0x2 - field public static final int CAUSE_INSTALL_COMPLETED = 1; // 0x1 - field public static final int CAUSE_NOT_SPECIFIED = 0; // 0x0 - field public static final int STATUS_IN_PROGRESS = 2; // 0x2 - field public static final int STATUS_IN_USE = 4; // 0x4 - field public static final int STATUS_NOT_STARTED = 1; // 0x1 - field public static final int STATUS_READY = 3; // 0x3 - field public static final int STATUS_UNKNOWN = 0; // 0x0 - } - - public static interface DynamicSystemClient.OnStatusChangedListener { - method public void onStatusChanged(int, int, long, @Nullable Throwable); - } - -} - package android.os.storage { public final class CrateInfo implements android.os.Parcelable { @@ -3290,10 +1364,6 @@ package android.os.storage { field @NonNull public static final android.os.Parcelable.Creator<android.os.storage.CrateInfo> CREATOR; } - public class StorageManager { - method public static boolean hasIsolatedStorage(); - } - public final class StorageVolume implements android.os.Parcelable { method public String getPath(); } @@ -3310,17 +1380,9 @@ package android.os.strictmode { package android.permission { public final class PermissionControllerManager { - method @RequiresPermission(anyOf={android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS, android.Manifest.permission.RESTORE_RUNTIME_PERMISSIONS}) public void applyStagedRuntimePermissionBackup(@NonNull String, @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>); method @RequiresPermission(android.Manifest.permission.GET_RUNTIME_PERMISSIONS) public void countPermissionApps(@NonNull java.util.List<java.lang.String>, int, @NonNull android.permission.PermissionControllerManager.OnCountPermissionAppsResultCallback, @Nullable android.os.Handler); method @RequiresPermission(android.Manifest.permission.GET_RUNTIME_PERMISSIONS) public void getAppPermissions(@NonNull String, @NonNull android.permission.PermissionControllerManager.OnGetAppPermissionResultCallback, @Nullable android.os.Handler); - method @RequiresPermission(android.Manifest.permission.GET_RUNTIME_PERMISSIONS) public void getRuntimePermissionBackup(@NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<byte[]>); method @RequiresPermission(android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS) public void revokeRuntimePermission(@NonNull String, @NonNull String); - method @RequiresPermission(android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS) public void revokeRuntimePermissions(@NonNull java.util.Map<java.lang.String,java.util.List<java.lang.String>>, boolean, int, @NonNull java.util.concurrent.Executor, @NonNull android.permission.PermissionControllerManager.OnRevokeRuntimePermissionsCallback); - method @RequiresPermission(anyOf={android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS, android.Manifest.permission.RESTORE_RUNTIME_PERMISSIONS}) public void stageAndApplyRuntimePermissionsBackup(@NonNull byte[], @NonNull android.os.UserHandle); - field public static final int COUNT_ONLY_WHEN_GRANTED = 1; // 0x1 - field public static final int COUNT_WHEN_SYSTEM = 2; // 0x2 - field public static final int REASON_INSTALLER_POLICY_VIOLATION = 2; // 0x2 - field public static final int REASON_MALWARE = 1; // 0x1 } public static interface PermissionControllerManager.OnCountPermissionAppsResultCallback { @@ -3331,33 +1393,6 @@ package android.permission { method public void onGetAppPermissions(@NonNull java.util.List<android.permission.RuntimePermissionPresentationInfo>); } - public abstract static class PermissionControllerManager.OnRevokeRuntimePermissionsCallback { - ctor public PermissionControllerManager.OnRevokeRuntimePermissionsCallback(); - method public abstract void onRevokeRuntimePermissions(@NonNull java.util.Map<java.lang.String,java.util.List<java.lang.String>>); - } - - public final class PermissionManager { - method @IntRange(from=0) @RequiresPermission(anyOf={android.Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY, android.Manifest.permission.UPGRADE_RUNTIME_PERMISSIONS}) public int getRuntimePermissionsVersion(); - method @NonNull public java.util.List<android.permission.PermissionManager.SplitPermissionInfo> getSplitPermissions(); - method @RequiresPermission(anyOf={android.Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY, android.Manifest.permission.UPGRADE_RUNTIME_PERMISSIONS}) public void setRuntimePermissionsVersion(@IntRange(from=0) int); - } - - public static final class PermissionManager.SplitPermissionInfo { - method @NonNull public java.util.List<java.lang.String> getNewPermissions(); - method @NonNull public String getSplitPermission(); - method public int getTargetSdk(); - } - - public final class RuntimePermissionPresentationInfo implements android.os.Parcelable { - ctor public RuntimePermissionPresentationInfo(@NonNull CharSequence, boolean, boolean); - method public int describeContents(); - method @NonNull public CharSequence getLabel(); - method public boolean isGranted(); - method public boolean isStandard(); - method public void writeToParcel(android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.permission.RuntimePermissionPresentationInfo> CREATOR; - } - } package android.print { @@ -3405,86 +1440,24 @@ package android.provider { } public final class DeviceConfig { - method @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public static void addOnPropertiesChangedListener(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.provider.DeviceConfig.OnPropertiesChangedListener); - method @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public static boolean getBoolean(@NonNull String, @NonNull String, boolean); - method @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public static float getFloat(@NonNull String, @NonNull String, float); - method @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public static int getInt(@NonNull String, @NonNull String, int); - method @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public static long getLong(@NonNull String, @NonNull String, long); - method @NonNull @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public static android.provider.DeviceConfig.Properties getProperties(@NonNull String, @NonNull java.lang.String...); - method @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public static String getProperty(@NonNull String, @NonNull String); - method @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public static String getString(@NonNull String, @NonNull String, @Nullable String); - method public static void removeOnPropertiesChangedListener(@NonNull android.provider.DeviceConfig.OnPropertiesChangedListener); - method @RequiresPermission(android.Manifest.permission.WRITE_DEVICE_CONFIG) public static void resetToDefaults(int, @Nullable String); - method @RequiresPermission(android.Manifest.permission.WRITE_DEVICE_CONFIG) public static boolean setProperties(@NonNull android.provider.DeviceConfig.Properties) throws android.provider.DeviceConfig.BadConfigException; - method @RequiresPermission(android.Manifest.permission.WRITE_DEVICE_CONFIG) public static boolean setProperty(@NonNull String, @NonNull String, @Nullable String, boolean); field public static final String NAMESPACE_ALARM_MANAGER = "alarm_manager"; field public static final String NAMESPACE_ANDROID = "android"; - field public static final String NAMESPACE_AUTOFILL = "autofill"; - field public static final String NAMESPACE_BIOMETRICS = "biometrics"; - field public static final String NAMESPACE_CONTENT_CAPTURE = "content_capture"; field public static final String NAMESPACE_DEVICE_IDLE = "device_idle"; field public static final String NAMESPACE_JOB_SCHEDULER = "jobscheduler"; - field public static final String NAMESPACE_PERMISSIONS = "permissions"; - field public static final String NAMESPACE_PRIVACY = "privacy"; - field public static final String NAMESPACE_ROLLBACK = "rollback"; - field public static final String NAMESPACE_ROLLBACK_BOOT = "rollback_boot"; - } - - public static class DeviceConfig.BadConfigException extends java.lang.Exception { - ctor public DeviceConfig.BadConfigException(); - } - - public static interface DeviceConfig.OnPropertiesChangedListener { - method public void onPropertiesChanged(@NonNull android.provider.DeviceConfig.Properties); - } - - public static class DeviceConfig.Properties { - method public boolean getBoolean(@NonNull String, boolean); - method public float getFloat(@NonNull String, float); - method public int getInt(@NonNull String, int); - method @NonNull public java.util.Set<java.lang.String> getKeyset(); - method public long getLong(@NonNull String, long); - method @NonNull public String getNamespace(); - method @Nullable public String getString(@NonNull String, @Nullable String); - } - - public static final class DeviceConfig.Properties.Builder { - ctor public DeviceConfig.Properties.Builder(@NonNull String); - method @NonNull public android.provider.DeviceConfig.Properties build(); - method @NonNull public android.provider.DeviceConfig.Properties.Builder setBoolean(@NonNull String, boolean); - method @NonNull public android.provider.DeviceConfig.Properties.Builder setFloat(@NonNull String, float); - method @NonNull public android.provider.DeviceConfig.Properties.Builder setInt(@NonNull String, int); - method @NonNull public android.provider.DeviceConfig.Properties.Builder setLong(@NonNull String, long); - method @NonNull public android.provider.DeviceConfig.Properties.Builder setString(@NonNull String, @Nullable String); - } - - public final class DocumentsContract { - method public static boolean isManageMode(@NonNull android.net.Uri); - method @NonNull public static android.net.Uri setManageMode(@NonNull android.net.Uri); - } - - public final class MediaStore { - method @NonNull @WorkerThread public static android.net.Uri scanFile(@NonNull android.content.ContentResolver, @NonNull java.io.File); - method @WorkerThread public static void scanVolume(@NonNull android.content.ContentResolver, @NonNull String); - method @WorkerThread public static void waitForIdle(@NonNull android.content.ContentResolver); } public final class Settings { - field public static final String ACTION_ENTERPRISE_PRIVACY_SETTINGS = "android.settings.ENTERPRISE_PRIVACY_SETTINGS"; - field public static final String ACTION_MANAGE_APP_OVERLAY_PERMISSION = "android.settings.MANAGE_APP_OVERLAY_PERMISSION"; - field public static final String ACTION_REQUEST_ENABLE_CONTENT_CAPTURE = "android.settings.REQUEST_ENABLE_CONTENT_CAPTURE"; - field public static final String ACTION_TETHER_PROVISIONING_UI = "android.settings.TETHER_PROVISIONING_UI"; field public static final int RESET_MODE_PACKAGE_DEFAULTS = 1; // 0x1 } public static final class Settings.Global extends android.provider.Settings.NameValueTable { field public static final String APP_OPS_CONSTANTS = "app_ops_constants"; - field public static final String AUTOFILL_COMPAT_MODE_ALLOWED_PACKAGES = "autofill_compat_mode_allowed_packages"; field public static final String AUTOMATIC_POWER_SAVE_MODE = "automatic_power_save_mode"; field public static final String BATTERY_SAVER_CONSTANTS = "battery_saver_constants"; field public static final String DYNAMIC_POWER_SAVINGS_DISABLE_THRESHOLD = "dynamic_power_savings_disable_threshold"; field public static final String DYNAMIC_POWER_SAVINGS_ENABLED = "dynamic_power_savings_enabled"; field public static final String HIDDEN_API_BLACKLIST_EXEMPTIONS = "hidden_api_blacklist_exemptions"; + field public static final String HIDDEN_API_POLICY = "hidden_api_policy"; field public static final String HIDE_ERROR_DIALOGS = "hide_error_dialogs"; field public static final String LOCATION_GLOBAL_KILL_SWITCH = "location_global_kill_switch"; field public static final String LOCATION_IGNORE_SETTINGS_PACKAGE_WHITELIST = "location_ignore_settings_package_whitelist"; @@ -3493,12 +1466,10 @@ package android.provider { field public static final String NOTIFICATION_BUBBLES = "notification_bubbles"; field public static final String OVERLAY_DISPLAY_DEVICES = "overlay_display_devices"; field public static final String SHOW_FIRST_CRASH_DIALOG = "show_first_crash_dialog"; - field public static final String TETHER_OFFLOAD_DISABLED = "tether_offload_disabled"; field public static final String USE_OPEN_WIFI_PACKAGE = "use_open_wifi_package"; } public static final class Settings.Secure extends android.provider.Settings.NameValueTable { - method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public static void resetToDefaults(@NonNull android.content.ContentResolver, @Nullable String); field public static final String ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED = "accessibility_display_magnification_enabled"; field public static final String ACCESSIBILITY_MAGNIFICATION_CAPABILITY = "accessibility_magnification_capability"; field public static final String ACCESSIBILITY_MAGNIFICATION_MODE = "accessibility_magnification_mode"; @@ -3507,23 +1478,12 @@ package android.provider { field public static final int ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW = 2; // 0x2 field public static final String ACCESSIBILITY_SHORTCUT_TARGET_SERVICE = "accessibility_shortcut_target_service"; field public static final String ANR_SHOW_BACKGROUND = "anr_show_background"; - field public static final String AUTOFILL_FEATURE_FIELD_CLASSIFICATION = "autofill_field_classification"; field public static final String AUTOFILL_SERVICE = "autofill_service"; - field public static final String AUTOFILL_USER_DATA_MAX_CATEGORY_COUNT = "autofill_user_data_max_category_count"; - field public static final String AUTOFILL_USER_DATA_MAX_FIELD_CLASSIFICATION_IDS_SIZE = "autofill_user_data_max_field_classification_size"; - field public static final String AUTOFILL_USER_DATA_MAX_USER_DATA_SIZE = "autofill_user_data_max_user_data_size"; - field public static final String AUTOFILL_USER_DATA_MAX_VALUE_LENGTH = "autofill_user_data_max_value_length"; - field public static final String AUTOFILL_USER_DATA_MIN_VALUE_LENGTH = "autofill_user_data_min_value_length"; field public static final String CONTENT_CAPTURE_ENABLED = "content_capture_enabled"; field public static final String DISABLED_PRINT_SERVICES = "disabled_print_services"; - field public static final String DOZE_ALWAYS_ON = "doze_always_on"; field @Deprecated public static final String ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES = "enabled_notification_policy_access_packages"; field public static final String ENABLED_VR_LISTENERS = "enabled_vr_listeners"; field public static final String IMMERSIVE_MODE_CONFIRMATIONS = "immersive_mode_confirmations"; - field public static final String LOCATION_ACCESS_CHECK_DELAY_MILLIS = "location_access_check_delay_millis"; - field public static final String LOCATION_ACCESS_CHECK_INTERVAL_MILLIS = "location_access_check_interval_millis"; - field public static final String LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS = "lock_screen_allow_private_notifications"; - field public static final String LOCK_SCREEN_SHOW_NOTIFICATIONS = "lock_screen_show_notifications"; field public static final String NFC_PAYMENT_DEFAULT_COMPONENT = "nfc_payment_default_component"; field public static final String NOTIFICATION_BADGING = "notification_badging"; field public static final String POWER_MENU_LOCKED_SHOW_CONTENT = "power_menu_locked_show_content"; @@ -3532,45 +1492,9 @@ package android.provider { field public static final String SHOW_FIRST_CRASH_DIALOG_DEV_OPTION = "show_first_crash_dialog_dev_option"; field public static final String SHOW_IME_WITH_HARD_KEYBOARD = "show_ime_with_hard_keyboard"; field @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public static final String SYNC_PARENT_SOUNDS = "sync_parent_sounds"; - field public static final String USER_SETUP_COMPLETE = "user_setup_complete"; field public static final String VOICE_INTERACTION_SERVICE = "voice_interaction_service"; } - public static final class Telephony.CellBroadcasts implements android.provider.BaseColumns { - field public static final String CID = "cid"; - field public static final String CMAS_CATEGORY = "cmas_category"; - field public static final String CMAS_CERTAINTY = "cmas_certainty"; - field public static final String CMAS_MESSAGE_CLASS = "cmas_message_class"; - field public static final String CMAS_RESPONSE_TYPE = "cmas_response_type"; - field public static final String CMAS_SEVERITY = "cmas_severity"; - field public static final String CMAS_URGENCY = "cmas_urgency"; - field @NonNull public static final android.net.Uri CONTENT_URI; - field public static final String DATA_CODING_SCHEME = "dcs"; - field public static final String DEFAULT_SORT_ORDER = "date DESC"; - field public static final String DELIVERY_TIME = "date"; - field public static final String ETWS_IS_PRIMARY = "etws_is_primary"; - field public static final String ETWS_WARNING_TYPE = "etws_warning_type"; - field public static final String GEOGRAPHICAL_SCOPE = "geo_scope"; - field public static final String GEOMETRIES = "geometries"; - field public static final String LAC = "lac"; - field public static final String LANGUAGE_CODE = "language"; - field public static final String LOCATION_CHECK_TIME = "location_check_time"; - field public static final String MAXIMUM_WAIT_TIME = "maximum_wait_time"; - field public static final String MESSAGE_BODY = "body"; - field public static final String MESSAGE_BROADCASTED = "message_broadcasted"; - field public static final String MESSAGE_DISPLAYED = "message_displayed"; - field public static final String MESSAGE_FORMAT = "format"; - field @NonNull @RequiresPermission(android.Manifest.permission.READ_CELL_BROADCASTS) public static final android.net.Uri MESSAGE_HISTORY_URI; - field public static final String MESSAGE_PRIORITY = "priority"; - field public static final String MESSAGE_READ = "read"; - field public static final String PLMN = "plmn"; - field public static final String RECEIVED_TIME = "received_time"; - field public static final String SERIAL_NUMBER = "serial_number"; - field public static final String SERVICE_CATEGORY = "service_category"; - field public static final String SLOT_INDEX = "slot_index"; - field public static final String SUBSCRIPTION_ID = "sub_id"; - } - public static final class Telephony.Sms.Intents { field public static final String SMS_CARRIER_PROVISION_ACTION = "android.provider.Telephony.SMS_CARRIER_PROVISION"; } @@ -3592,19 +1516,6 @@ package android.security { package android.security.keystore { - public abstract class AttestationUtils { - method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static java.security.cert.X509Certificate[] attestDeviceIds(android.content.Context, @NonNull int[], @NonNull byte[]) throws android.security.keystore.DeviceIdAttestationException; - field public static final int ID_TYPE_IMEI = 2; // 0x2 - field public static final int ID_TYPE_MEID = 3; // 0x3 - field public static final int ID_TYPE_SERIAL = 1; // 0x1 - field public static final int USE_INDIVIDUAL_ATTESTATION = 4; // 0x4 - } - - public class DeviceIdAttestationException extends java.lang.Exception { - ctor public DeviceIdAttestationException(@Nullable String); - ctor public DeviceIdAttestationException(@Nullable String, @Nullable Throwable); - } - public static final class KeyGenParameterSpec.Builder { method @NonNull public android.security.keystore.KeyGenParameterSpec.Builder setUniqueIdIncluded(boolean); } @@ -3619,37 +1530,8 @@ package android.security.keystore { } -package android.service.appprediction { - - public abstract class AppPredictionService extends android.app.Service { - ctor public AppPredictionService(); - method @MainThread public abstract void onAppTargetEvent(@NonNull android.app.prediction.AppPredictionSessionId, @NonNull android.app.prediction.AppTargetEvent); - method @NonNull public final android.os.IBinder onBind(@NonNull android.content.Intent); - method public void onCreatePredictionSession(@NonNull android.app.prediction.AppPredictionContext, @NonNull android.app.prediction.AppPredictionSessionId); - method @MainThread public void onDestroyPredictionSession(@NonNull android.app.prediction.AppPredictionSessionId); - method @MainThread public abstract void onLaunchLocationShown(@NonNull android.app.prediction.AppPredictionSessionId, @NonNull String, @NonNull java.util.List<android.app.prediction.AppTargetId>); - method @MainThread public abstract void onRequestPredictionUpdate(@NonNull android.app.prediction.AppPredictionSessionId); - method @MainThread public abstract void onSortAppTargets(@NonNull android.app.prediction.AppPredictionSessionId, @NonNull java.util.List<android.app.prediction.AppTarget>, @NonNull android.os.CancellationSignal, @NonNull java.util.function.Consumer<java.util.List<android.app.prediction.AppTarget>>); - method @MainThread public void onStartPredictionUpdates(); - method @MainThread public void onStopPredictionUpdates(); - method public final void updatePredictions(@NonNull android.app.prediction.AppPredictionSessionId, @NonNull java.util.List<android.app.prediction.AppTarget>); - } - -} - package android.service.autofill { - public abstract class AutofillFieldClassificationService extends android.app.Service { - ctor public AutofillFieldClassificationService(); - method public android.os.IBinder onBind(android.content.Intent); - field public static final String REQUIRED_ALGORITHM_CREDIT_CARD = "CREDIT_CARD"; - field public static final String REQUIRED_ALGORITHM_EDIT_DISTANCE = "EDIT_DISTANCE"; - field public static final String REQUIRED_ALGORITHM_EXACT_MATCH = "EXACT_MATCH"; - field public static final String SERVICE_INTERFACE = "android.service.autofill.AutofillFieldClassificationService"; - field public static final String SERVICE_META_DATA_KEY_AVAILABLE_ALGORITHMS = "android.autofill.field_classification.available_algorithms"; - field public static final String SERVICE_META_DATA_KEY_DEFAULT_ALGORITHM = "android.autofill.field_classification.default_algorithm"; - } - public final class CharSequenceTransformation extends android.service.autofill.InternalTransformation implements android.os.Parcelable android.service.autofill.Transformation { method public void apply(@NonNull android.service.autofill.ValueFinder, @NonNull android.widget.RemoteViews, int) throws java.lang.Exception; } @@ -3672,11 +1554,6 @@ package android.service.autofill { method @Nullable public android.util.SparseArray<android.service.autofill.InternalOnClickAction> getActions(); } - public static final class Dataset.Builder { - ctor public Dataset.Builder(@NonNull android.service.autofill.InlinePresentation); - method @NonNull public android.service.autofill.Dataset.Builder setFieldInlinePresentation(@NonNull android.view.autofill.AutofillId, @Nullable android.view.autofill.AutofillValue, @Nullable java.util.regex.Pattern, @NonNull android.service.autofill.InlinePresentation); - } - public final class DateTransformation extends android.service.autofill.InternalTransformation implements android.os.Parcelable android.service.autofill.Transformation { method public void apply(@NonNull android.service.autofill.ValueFinder, @NonNull android.widget.RemoteViews, int) throws java.lang.Exception; } @@ -3693,15 +1570,6 @@ package android.service.autofill { method public void apply(@NonNull android.service.autofill.ValueFinder, @NonNull android.widget.RemoteViews, int) throws java.lang.Exception; } - public abstract class InlineSuggestionRenderService extends android.app.Service { - ctor public InlineSuggestionRenderService(); - method @Nullable public final android.os.IBinder onBind(@NonNull android.content.Intent); - method @NonNull public android.os.Bundle onGetInlineSuggestionsRendererInfo(); - method @Nullable public android.view.View onRenderSuggestion(@NonNull android.service.autofill.InlinePresentation, int, int); - method public final void startIntentSender(@NonNull android.content.IntentSender); - field public static final String SERVICE_INTERFACE = "android.service.autofill.InlineSuggestionRenderService"; - } - public abstract class InternalOnClickAction implements android.service.autofill.OnClickAction android.os.Parcelable { ctor public InternalOnClickAction(); method public abstract void onClick(@NonNull android.view.ViewGroup); @@ -3751,26 +1619,6 @@ package android.service.autofill { package android.service.autofill.augmented { - public abstract class AugmentedAutofillService extends android.app.Service { - ctor public AugmentedAutofillService(); - method protected final void dump(java.io.FileDescriptor, java.io.PrintWriter, String[]); - method protected void dump(@NonNull java.io.PrintWriter, @NonNull String[]); - method @Nullable public final android.service.autofill.FillEventHistory getFillEventHistory(); - method public void onConnected(); - method public void onDisconnected(); - method public void onFillRequest(@NonNull android.service.autofill.augmented.FillRequest, @NonNull android.os.CancellationSignal, @NonNull android.service.autofill.augmented.FillController, @NonNull android.service.autofill.augmented.FillCallback); - method public final boolean requestAutofill(@NonNull android.content.ComponentName, @NonNull android.view.autofill.AutofillId); - field public static final String SERVICE_INTERFACE = "android.service.autofill.augmented.AugmentedAutofillService"; - } - - public final class FillCallback { - method public void onSuccess(@Nullable android.service.autofill.augmented.FillResponse); - } - - public final class FillController { - method public void autofill(@NonNull java.util.List<android.util.Pair<android.view.autofill.AutofillId,android.view.autofill.AutofillValue>>); - } - public final class FillRequest { method @NonNull public android.content.ComponentName getActivityComponent(); method @NonNull public android.view.autofill.AutofillId getFocusedId(); @@ -3780,181 +1628,14 @@ package android.service.autofill.augmented { method public int getTaskId(); } - public final class FillResponse { - } - - public static final class FillResponse.Builder { - ctor public FillResponse.Builder(); - method @NonNull public android.service.autofill.augmented.FillResponse build(); - method @NonNull public android.service.autofill.augmented.FillResponse.Builder setClientState(@NonNull android.os.Bundle); - method @NonNull public android.service.autofill.augmented.FillResponse.Builder setFillWindow(@NonNull android.service.autofill.augmented.FillWindow); - method @NonNull public android.service.autofill.augmented.FillResponse.Builder setInlineSuggestions(@NonNull java.util.List<android.service.autofill.Dataset>); - } - - public final class FillWindow implements java.lang.AutoCloseable { - ctor public FillWindow(); - method public void destroy(); - method public boolean update(@NonNull android.service.autofill.augmented.PresentationParams.Area, @NonNull android.view.View, long); - } - - public abstract class PresentationParams { - method @Nullable public android.service.autofill.augmented.PresentationParams.Area getSuggestionArea(); - } - - public abstract static class PresentationParams.Area { - method @NonNull public android.graphics.Rect getBounds(); - } - -} - -package android.service.contentcapture { - - public final class ActivityEvent implements android.os.Parcelable { - method public int describeContents(); - method @NonNull public android.content.ComponentName getComponentName(); - method public int getEventType(); - method public void writeToParcel(@NonNull android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.service.contentcapture.ActivityEvent> CREATOR; - field public static final int TYPE_ACTIVITY_DESTROYED = 24; // 0x18 - field public static final int TYPE_ACTIVITY_PAUSED = 2; // 0x2 - field public static final int TYPE_ACTIVITY_RESUMED = 1; // 0x1 - field public static final int TYPE_ACTIVITY_STOPPED = 23; // 0x17 - } - - public abstract class ContentCaptureService extends android.app.Service { - ctor public ContentCaptureService(); - method public final void disableSelf(); - method public void onActivityEvent(@NonNull android.service.contentcapture.ActivityEvent); - method public void onActivitySnapshot(@NonNull android.view.contentcapture.ContentCaptureSessionId, @NonNull android.service.contentcapture.SnapshotData); - method public void onConnected(); - method public void onContentCaptureEvent(@NonNull android.view.contentcapture.ContentCaptureSessionId, @NonNull android.view.contentcapture.ContentCaptureEvent); - method public void onCreateContentCaptureSession(@NonNull android.view.contentcapture.ContentCaptureContext, @NonNull android.view.contentcapture.ContentCaptureSessionId); - method public void onDataRemovalRequest(@NonNull android.view.contentcapture.DataRemovalRequest); - method public void onDataShareRequest(@NonNull android.view.contentcapture.DataShareRequest, @NonNull android.service.contentcapture.DataShareCallback); - method public void onDestroyContentCaptureSession(@NonNull android.view.contentcapture.ContentCaptureSessionId); - method public void onDisconnected(); - method public final void setContentCaptureConditions(@NonNull String, @Nullable java.util.Set<android.view.contentcapture.ContentCaptureCondition>); - method public final void setContentCaptureWhitelist(@Nullable java.util.Set<java.lang.String>, @Nullable java.util.Set<android.content.ComponentName>); - field public static final String SERVICE_INTERFACE = "android.service.contentcapture.ContentCaptureService"; - field public static final String SERVICE_META_DATA = "android.content_capture"; - } - - public interface DataShareCallback { - method public void onAccept(@NonNull java.util.concurrent.Executor, @NonNull android.service.contentcapture.DataShareReadAdapter); - method public void onReject(); - } - - public interface DataShareReadAdapter { - method public void onError(int); - method public void onStart(@NonNull android.os.ParcelFileDescriptor); - } - - public final class SnapshotData implements android.os.Parcelable { - method public int describeContents(); - method @Nullable public android.app.assist.AssistContent getAssistContent(); - method @NonNull public android.os.Bundle getAssistData(); - method @NonNull public android.app.assist.AssistStructure getAssistStructure(); - method public void writeToParcel(@NonNull android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.service.contentcapture.SnapshotData> CREATOR; - } - } package android.service.notification { - public final class Adjustment implements android.os.Parcelable { - ctor public Adjustment(String, String, android.os.Bundle, CharSequence, int); - ctor public Adjustment(@NonNull String, @NonNull String, @NonNull android.os.Bundle, @NonNull CharSequence, @NonNull android.os.UserHandle); - method public int describeContents(); - method @NonNull public CharSequence getExplanation(); - method @NonNull public String getKey(); - method @NonNull public String getPackage(); - method @NonNull public android.os.Bundle getSignals(); - method public int getUser(); - method @NonNull public android.os.UserHandle getUserHandle(); - method public void writeToParcel(android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.service.notification.Adjustment> CREATOR; - field public static final String KEY_CONTEXTUAL_ACTIONS = "key_contextual_actions"; - field public static final String KEY_IMPORTANCE = "key_importance"; - field public static final String KEY_RANKING_SCORE = "key_ranking_score"; - field public static final String KEY_SNOOZE_CRITERIA = "key_snooze_criteria"; - field public static final String KEY_TEXT_REPLIES = "key_text_replies"; - field public static final String KEY_USER_SENTIMENT = "key_user_sentiment"; - } - @Deprecated public abstract class ConditionProviderService extends android.app.Service { method @Deprecated public boolean isBound(); } - public abstract class NotificationAssistantService extends android.service.notification.NotificationListenerService { - ctor public NotificationAssistantService(); - method public final void adjustNotification(@NonNull android.service.notification.Adjustment); - method public final void adjustNotifications(@NonNull java.util.List<android.service.notification.Adjustment>); - method public void onActionInvoked(@NonNull String, @NonNull android.app.Notification.Action, int); - method public void onAllowedAdjustmentsChanged(); - method @NonNull public final android.os.IBinder onBind(@Nullable android.content.Intent); - method public void onNotificationDirectReplied(@NonNull String); - method @Nullable public abstract android.service.notification.Adjustment onNotificationEnqueued(@NonNull android.service.notification.StatusBarNotification); - method @Nullable public android.service.notification.Adjustment onNotificationEnqueued(@NonNull android.service.notification.StatusBarNotification, @NonNull android.app.NotificationChannel); - method public void onNotificationExpansionChanged(@NonNull String, boolean, boolean); - method public abstract void onNotificationSnoozedUntilContext(@NonNull android.service.notification.StatusBarNotification, @NonNull String); - method public void onNotificationVisibilityChanged(@NonNull String, boolean); - method public void onNotificationsSeen(@NonNull java.util.List<java.lang.String>); - method public void onPanelHidden(); - method public void onPanelRevealed(int); - method public void onSuggestedReplySent(@NonNull String, @NonNull CharSequence, int); - method public final void unsnoozeNotification(@NonNull String); - field public static final String SERVICE_INTERFACE = "android.service.notification.NotificationAssistantService"; - field public static final int SOURCE_FROM_APP = 0; // 0x0 - field public static final int SOURCE_FROM_ASSISTANT = 1; // 0x1 - } - - public abstract class NotificationListenerService extends android.app.Service { - method public void onNotificationRemoved(@NonNull android.service.notification.StatusBarNotification, @NonNull android.service.notification.NotificationListenerService.RankingMap, @NonNull android.service.notification.NotificationStats, int); - } - - public final class NotificationStats implements android.os.Parcelable { - ctor public NotificationStats(); - method public int describeContents(); - method public int getDismissalSentiment(); - method public int getDismissalSurface(); - method public boolean hasDirectReplied(); - method public boolean hasExpanded(); - method public boolean hasInteracted(); - method public boolean hasSeen(); - method public boolean hasSnoozed(); - method public boolean hasViewedSettings(); - method public void setDirectReplied(); - method public void setDismissalSentiment(int); - method public void setDismissalSurface(int); - method public void setExpanded(); - method public void setSeen(); - method public void setSnoozed(); - method public void setViewedSettings(); - method public void writeToParcel(android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.service.notification.NotificationStats> CREATOR; - field public static final int DISMISSAL_AOD = 2; // 0x2 - field public static final int DISMISSAL_NOT_DISMISSED = -1; // 0xffffffff - field public static final int DISMISSAL_OTHER = 0; // 0x0 - field public static final int DISMISSAL_PEEK = 1; // 0x1 - field public static final int DISMISSAL_SHADE = 3; // 0x3 - field public static final int DISMISS_SENTIMENT_NEGATIVE = 0; // 0x0 - field public static final int DISMISS_SENTIMENT_NEUTRAL = 1; // 0x1 - field public static final int DISMISS_SENTIMENT_POSITIVE = 2; // 0x2 - field public static final int DISMISS_SENTIMENT_UNKNOWN = -1000; // 0xfffffc18 - } - - public final class SnoozeCriterion implements android.os.Parcelable { - ctor public SnoozeCriterion(String, CharSequence, CharSequence); - ctor protected SnoozeCriterion(android.os.Parcel); - method public int describeContents(); - method public CharSequence getConfirmation(); - method public CharSequence getExplanation(); - method public String getId(); - method public void writeToParcel(android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.service.notification.SnoozeCriterion> CREATOR; - } - } package android.service.quickaccesswallet { @@ -3995,67 +1676,16 @@ package android.service.quicksettings { } -package android.service.textclassifier { - - public abstract class TextClassifierService extends android.app.Service { - ctor public TextClassifierService(); - method @NonNull public static android.view.textclassifier.TextClassifier getDefaultTextClassifierImplementation(@NonNull android.content.Context); - method @Deprecated public final android.view.textclassifier.TextClassifier getLocalTextClassifier(); - method @Nullable public final android.os.IBinder onBind(@NonNull android.content.Intent); - method @MainThread public abstract void onClassifyText(@Nullable android.view.textclassifier.TextClassificationSessionId, @NonNull android.view.textclassifier.TextClassification.Request, @NonNull android.os.CancellationSignal, @NonNull android.service.textclassifier.TextClassifierService.Callback<android.view.textclassifier.TextClassification>); - method public void onConnected(); - method @MainThread public void onCreateTextClassificationSession(@NonNull android.view.textclassifier.TextClassificationContext, @NonNull android.view.textclassifier.TextClassificationSessionId); - method @MainThread public void onDestroyTextClassificationSession(@NonNull android.view.textclassifier.TextClassificationSessionId); - method @MainThread public void onDetectLanguage(@Nullable android.view.textclassifier.TextClassificationSessionId, @NonNull android.view.textclassifier.TextLanguage.Request, @NonNull android.os.CancellationSignal, @NonNull android.service.textclassifier.TextClassifierService.Callback<android.view.textclassifier.TextLanguage>); - method public void onDisconnected(); - method @MainThread public abstract void onGenerateLinks(@Nullable android.view.textclassifier.TextClassificationSessionId, @NonNull android.view.textclassifier.TextLinks.Request, @NonNull android.os.CancellationSignal, @NonNull android.service.textclassifier.TextClassifierService.Callback<android.view.textclassifier.TextLinks>); - method @Deprecated @MainThread public void onSelectionEvent(@Nullable android.view.textclassifier.TextClassificationSessionId, @NonNull android.view.textclassifier.SelectionEvent); - method @MainThread public void onSuggestConversationActions(@Nullable android.view.textclassifier.TextClassificationSessionId, @NonNull android.view.textclassifier.ConversationActions.Request, @NonNull android.os.CancellationSignal, @NonNull android.service.textclassifier.TextClassifierService.Callback<android.view.textclassifier.ConversationActions>); - method @MainThread public abstract void onSuggestSelection(@Nullable android.view.textclassifier.TextClassificationSessionId, @NonNull android.view.textclassifier.TextSelection.Request, @NonNull android.os.CancellationSignal, @NonNull android.service.textclassifier.TextClassifierService.Callback<android.view.textclassifier.TextSelection>); - method @MainThread public void onTextClassifierEvent(@Nullable android.view.textclassifier.TextClassificationSessionId, @NonNull android.view.textclassifier.TextClassifierEvent); - field public static final String SERVICE_INTERFACE = "android.service.textclassifier.TextClassifierService"; - } - - public static interface TextClassifierService.Callback<T> { - method public void onFailure(@NonNull CharSequence); - method public void onSuccess(T); - } - -} - package android.service.watchdog { public abstract class ExplicitHealthCheckService extends android.app.Service { - ctor public ExplicitHealthCheckService(); - method public final void notifyHealthCheckPassed(@NonNull String); - method @NonNull public final android.os.IBinder onBind(@NonNull android.content.Intent); - method public abstract void onCancelHealthCheck(@NonNull String); - method @NonNull public abstract java.util.List<java.lang.String> onGetRequestedPackages(); - method @NonNull public abstract java.util.List<android.service.watchdog.ExplicitHealthCheckService.PackageConfig> onGetSupportedPackages(); - method public abstract void onRequestHealthCheck(@NonNull String); method public void setCallback(@Nullable android.os.RemoteCallback); - field public static final String BIND_PERMISSION = "android.permission.BIND_EXPLICIT_HEALTH_CHECK_SERVICE"; - field public static final String SERVICE_INTERFACE = "android.service.watchdog.ExplicitHealthCheckService"; - } - - public static final class ExplicitHealthCheckService.PackageConfig implements android.os.Parcelable { - ctor public ExplicitHealthCheckService.PackageConfig(@NonNull String, long); - method public int describeContents(); - method public long getHealthCheckTimeoutMillis(); - method @NonNull public String getPackageName(); - method public void writeToParcel(android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.service.watchdog.ExplicitHealthCheckService.PackageConfig> CREATOR; } } package android.telecom { - public final class Call { - method public void enterBackgroundAudioProcessing(); - method public void exitBackgroundAudioProcessing(boolean); - } - public static class Call.Details { method public String getTelecomCallId(); } @@ -4064,42 +1694,6 @@ package android.telecom { ctor public CallAudioState(boolean, int, int, @Nullable android.bluetooth.BluetoothDevice, @NonNull java.util.Collection<android.bluetooth.BluetoothDevice>); } - public static class CallScreeningService.CallResponse.Builder { - method @NonNull @RequiresPermission(android.Manifest.permission.CAPTURE_AUDIO_OUTPUT) public android.telecom.CallScreeningService.CallResponse.Builder setShouldScreenCallViaAudioProcessing(boolean); - } - - public abstract class Conference extends android.telecom.Conferenceable { - method public android.telecom.Connection getPrimaryConnection(); - method @NonNull public final String getTelecomCallId(); - method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public final void setAddress(@NonNull android.net.Uri, int); - method public final void setCallerDisplayName(@NonNull String, int); - method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setConferenceState(boolean); - } - - public abstract class Connection extends android.telecom.Conferenceable { - method @IntRange(from=0) public final long getConnectTimeMillis(); - method public final long getConnectionStartElapsedRealtimeMillis(); - method @Nullable public android.telecom.PhoneAccountHandle getPhoneAccountHandle(); - method @Nullable public final String getTelecomCallId(); - method public final void resetConnectionTime(); - method public void setCallDirection(int); - method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public final void setConnectTimeMillis(@IntRange(from=0) long); - method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public final void setConnectionStartElapsedRealtimeMillis(long); - method public void setPhoneAccountHandle(@NonNull android.telecom.PhoneAccountHandle); - method public void setTelecomCallId(@NonNull String); - field public static final int CAPABILITY_CONFERENCE_HAS_NO_CHILDREN = 2097152; // 0x200000 - field public static final int CAPABILITY_SPEED_UP_MT_AUDIO = 262144; // 0x40000 - field public static final String EXTRA_DISABLE_ADD_CALL = "android.telecom.extra.DISABLE_ADD_CALL"; - field public static final int PROPERTY_EMERGENCY_CALLBACK_MODE = 1; // 0x1 - field public static final int PROPERTY_GENERIC_CONFERENCE = 2; // 0x2 - field public static final int PROPERTY_IS_DOWNGRADED_CONFERENCE = 64; // 0x40 - field public static final int PROPERTY_REMOTELY_HOSTED = 2048; // 0x800 - } - - public final class ConnectionRequest implements android.os.Parcelable { - method @Nullable public String getTelecomCallId(); - } - public static final class ConnectionRequest.Builder { ctor public ConnectionRequest.Builder(); method @NonNull public android.telecom.ConnectionRequest build(); @@ -4115,53 +1709,11 @@ package android.telecom { method @NonNull public android.telecom.ConnectionRequest.Builder setVideoState(int); } - public static class PhoneAccount.Builder { - method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public android.telecom.PhoneAccount.Builder setGroupId(@NonNull String); - } - - public class PhoneAccountSuggestionService extends android.app.Service { - ctor public PhoneAccountSuggestionService(); - method public void onAccountSuggestionRequest(@NonNull String); - method public android.os.IBinder onBind(android.content.Intent); - method public final void suggestPhoneAccounts(@NonNull String, @NonNull java.util.List<android.telecom.PhoneAccountSuggestion>); - field public static final String SERVICE_INTERFACE = "android.telecom.PhoneAccountSuggestionService"; - } - - public class TelecomManager { - method @NonNull public android.content.Intent createLaunchEmergencyDialerIntent(@Nullable String); - method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public java.util.List<android.telecom.PhoneAccountHandle> getCallCapablePhoneAccounts(boolean); - method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getCurrentTtyMode(); - method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getDefaultDialerPackage(@NonNull android.os.UserHandle); - method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean isInEmergencyCall(); - method @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isRinging(); - method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setUserSelectedOutgoingPhoneAccount(@Nullable android.telecom.PhoneAccountHandle); - field public static final String ACTION_CURRENT_TTY_MODE_CHANGED = "android.telecom.action.CURRENT_TTY_MODE_CHANGED"; - field public static final String ACTION_TTY_PREFERRED_MODE_CHANGED = "android.telecom.action.TTY_PREFERRED_MODE_CHANGED"; - field public static final String EXTRA_CURRENT_TTY_MODE = "android.telecom.extra.CURRENT_TTY_MODE"; - field public static final String EXTRA_TTY_PREFERRED_MODE = "android.telecom.extra.TTY_PREFERRED_MODE"; - field public static final int TTY_MODE_FULL = 1; // 0x1 - field public static final int TTY_MODE_HCO = 2; // 0x2 - field public static final int TTY_MODE_OFF = 0; // 0x0 - field public static final int TTY_MODE_VCO = 3; // 0x3 - } - } package android.telephony { - public final class AccessNetworkConstants { - field public static final int TRANSPORT_TYPE_INVALID = -1; // 0xffffffff - } - - public static final class AccessNetworkConstants.NgranBands { - method public static int getFrequencyRangeGroup(int); - field public static final int FREQUENCY_RANGE_GROUP_1 = 1; // 0x1 - field public static final int FREQUENCY_RANGE_GROUP_2 = 2; // 0x2 - field public static final int FREQUENCY_RANGE_GROUP_UNKNOWN = 0; // 0x0 - } - public final class BarringInfo implements android.os.Parcelable { - ctor public BarringInfo(); ctor public BarringInfo(@Nullable android.telephony.CellIdentity, @NonNull android.util.SparseArray<android.telephony.BarringInfo.BarringServiceInfo>); } @@ -4169,57 +1721,6 @@ package android.telephony { ctor public BarringInfo.BarringServiceInfo(int, boolean, int, int); } - public final class CallQuality implements android.os.Parcelable { - ctor public CallQuality(int, int, int, int, int, int, int, int, int, int, int); - ctor public CallQuality(int, int, int, int, int, int, int, int, int, int, int, boolean, boolean, boolean); - method public int describeContents(); - method public int getAverageRelativeJitter(); - method public int getAverageRoundTripTime(); - method public int getCallDuration(); - method public int getCodecType(); - method public int getDownlinkCallQualityLevel(); - method public int getMaxRelativeJitter(); - method public int getNumRtpPacketsNotReceived(); - method public int getNumRtpPacketsReceived(); - method public int getNumRtpPacketsTransmitted(); - method public int getNumRtpPacketsTransmittedLost(); - method public int getUplinkCallQualityLevel(); - method public boolean isIncomingSilenceDetectedAtCallSetup(); - method public boolean isOutgoingSilenceDetectedAtCallSetup(); - method public boolean isRtpInactivityDetected(); - method public void writeToParcel(android.os.Parcel, int); - field public static final int CALL_QUALITY_BAD = 4; // 0x4 - field public static final int CALL_QUALITY_EXCELLENT = 0; // 0x0 - field public static final int CALL_QUALITY_FAIR = 2; // 0x2 - field public static final int CALL_QUALITY_GOOD = 1; // 0x1 - field public static final int CALL_QUALITY_NOT_AVAILABLE = 5; // 0x5 - field public static final int CALL_QUALITY_POOR = 3; // 0x3 - field @NonNull public static final android.os.Parcelable.Creator<android.telephony.CallQuality> CREATOR; - } - - public class CarrierConfigManager { - method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void overrideConfig(int, @Nullable android.os.PersistableBundle); - } - - public final class DataSpecificRegistrationInfo implements android.os.Parcelable { - method public int describeContents(); - method @NonNull public android.telephony.LteVopsSupportInfo getLteVopsSupportInfo(); - method public void writeToParcel(android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.telephony.DataSpecificRegistrationInfo> CREATOR; - } - - public final class LteVopsSupportInfo implements android.os.Parcelable { - ctor public LteVopsSupportInfo(int, int); - method public int describeContents(); - method public int getEmcBearerSupport(); - method public int getVopsSupport(); - method public void writeToParcel(android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.telephony.LteVopsSupportInfo> CREATOR; - field public static final int LTE_STATUS_NOT_AVAILABLE = 1; // 0x1 - field public static final int LTE_STATUS_NOT_SUPPORTED = 3; // 0x3 - field public static final int LTE_STATUS_SUPPORTED = 2; // 0x2 - } - public class MbmsDownloadSession implements java.lang.AutoCloseable { field public static final String MBMS_DOWNLOAD_SERVICE_OVERRIDE_METADATA = "mbms-download-service-override"; } @@ -4232,52 +1733,16 @@ package android.telephony { field public static final String MBMS_STREAMING_SERVICE_OVERRIDE_METADATA = "mbms-streaming-service-override"; } - public final class NetworkRegistrationInfo implements android.os.Parcelable { - method @Nullable public android.telephony.DataSpecificRegistrationInfo getDataSpecificInfo(); - method public int getRegistrationState(); - method public int getRejectCause(); - method public int getRoamingType(); - method public boolean isEmergencyEnabled(); - method public void writeToParcel(android.os.Parcel, int); - field public static final int REGISTRATION_STATE_DENIED = 3; // 0x3 - field public static final int REGISTRATION_STATE_HOME = 1; // 0x1 - field public static final int REGISTRATION_STATE_NOT_REGISTERED_OR_SEARCHING = 0; // 0x0 - field public static final int REGISTRATION_STATE_NOT_REGISTERED_SEARCHING = 2; // 0x2 - field public static final int REGISTRATION_STATE_ROAMING = 5; // 0x5 - field public static final int REGISTRATION_STATE_UNKNOWN = 4; // 0x4 - } - - public static final class NetworkRegistrationInfo.Builder { - ctor public NetworkRegistrationInfo.Builder(); - method @NonNull public android.telephony.NetworkRegistrationInfo build(); - method @NonNull public android.telephony.NetworkRegistrationInfo.Builder setAccessNetworkTechnology(int); - method @NonNull public android.telephony.NetworkRegistrationInfo.Builder setAvailableServices(@NonNull java.util.List<java.lang.Integer>); - method @NonNull public android.telephony.NetworkRegistrationInfo.Builder setCellIdentity(@Nullable android.telephony.CellIdentity); - method @NonNull public android.telephony.NetworkRegistrationInfo.Builder setDomain(int); - method @NonNull public android.telephony.NetworkRegistrationInfo.Builder setEmergencyOnly(boolean); - method @NonNull public android.telephony.NetworkRegistrationInfo.Builder setRegisteredPlmn(@Nullable String); - method @NonNull public android.telephony.NetworkRegistrationInfo.Builder setRegistrationState(int); - method @NonNull public android.telephony.NetworkRegistrationInfo.Builder setRejectCause(int); - method @NonNull public android.telephony.NetworkRegistrationInfo.Builder setTransportType(int); + public final class ModemActivityInfo implements android.os.Parcelable { + ctor public ModemActivityInfo(long, int, int, @NonNull int[], int); + method public boolean isValid(); } public class PhoneNumberUtils { method public static int getMinMatchForTest(); - method @NonNull public static String getUsernameFromUriNumber(@NonNull String); - method public static boolean isUriNumber(@Nullable String); - method public static boolean isVoiceMailNumber(@NonNull android.content.Context, int, @Nullable String); method public static void setMinMatchForTest(int); } - public class PhoneStateListener { - method @Deprecated public void onOutgoingEmergencyCall(@NonNull android.telephony.emergency.EmergencyNumber); - method public void onOutgoingEmergencyCall(@NonNull android.telephony.emergency.EmergencyNumber, int); - method @Deprecated public void onOutgoingEmergencySms(@NonNull android.telephony.emergency.EmergencyNumber); - method public void onOutgoingEmergencySms(@NonNull android.telephony.emergency.EmergencyNumber, int); - field @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public static final int LISTEN_OUTGOING_EMERGENCY_CALL = 268435456; // 0x10000000 - field @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public static final int LISTEN_OUTGOING_EMERGENCY_SMS = 536870912; // 0x20000000 - } - public final class PreciseDataConnectionState implements android.os.Parcelable { ctor @Deprecated public PreciseDataConnectionState(int, int, int, @NonNull String, @Nullable android.net.LinkProperties, int); } @@ -4303,44 +1768,16 @@ package android.telephony { field public static final int SMS_CATEGORY_STANDARD_SHORT_CODE = 2; // 0x2 } - public class SubscriptionManager { - method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDefaultVoiceSubscriptionId(int); - field @NonNull public static final android.net.Uri ADVANCED_CALLING_ENABLED_CONTENT_URI; - field @NonNull public static final android.net.Uri VT_ENABLED_CONTENT_URI; - field @NonNull public static final android.net.Uri WFC_ENABLED_CONTENT_URI; - field @NonNull public static final android.net.Uri WFC_MODE_CONTENT_URI; - field @NonNull public static final android.net.Uri WFC_ROAMING_ENABLED_CONTENT_URI; - field @NonNull public static final android.net.Uri WFC_ROAMING_MODE_CONTENT_URI; - } - public class TelephonyManager { method public int addDevicePolicyOverrideApn(@NonNull android.content.Context, @NonNull android.telephony.data.ApnSetting); - method public int checkCarrierPrivilegesForPackage(String); - method @Nullable @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public android.content.ComponentName getAndUpdateDefaultRespondViaMessageApplication(); method public int getCarrierIdListVersion(); - method public java.util.List<java.lang.String> getCarrierPackageNamesForIntent(android.content.Intent); - method @Nullable @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public android.content.ComponentName getDefaultRespondViaMessageApplication(); method @NonNull public java.util.List<android.telephony.data.ApnSetting> getDevicePolicyOverrideApns(@NonNull android.content.Context); - method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getEmergencyNumberDbVersion(); method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public String getLine1AlphaTag(); method public android.util.Pair<java.lang.Integer,java.lang.Integer> getRadioHalVersion(); - method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isMobileDataPolicyEnabled(int); method public boolean modifyDevicePolicyOverrideApn(@NonNull android.content.Context, int, @NonNull android.telephony.data.ApnSetting); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void refreshUiccProfile(); - method @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public void resetOtaEmergencyNumberDbFilePath(); method @Deprecated public void setCarrierTestOverride(String, String, String, String, String, String, String); method public void setCarrierTestOverride(String, String, String, String, String, String, String, String, String); - method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setMobileDataPolicyEnabledStatus(int, boolean); - method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setSystemSelectionChannels(@NonNull java.util.List<android.telephony.RadioAccessSpecifier>, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>); - method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setSystemSelectionChannels(@NonNull java.util.List<android.telephony.RadioAccessSpecifier>); - method @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public void updateOtaEmergencyNumberDbFilePath(@NonNull android.os.ParcelFileDescriptor); - field public static final int CARRIER_PRIVILEGE_STATUS_ERROR_LOADING_RULES = -2; // 0xfffffffe - field public static final int CARRIER_PRIVILEGE_STATUS_HAS_ACCESS = 1; // 0x1 - field public static final int CARRIER_PRIVILEGE_STATUS_NO_ACCESS = 0; // 0x0 - field public static final int CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED = -1; // 0xffffffff - field public static final int INVALID_EMERGENCY_NUMBER_DB_VERSION = -1; // 0xffffffff - field public static final int MOBILE_DATA_POLICY_DATA_ON_NON_DEFAULT_DURING_VOICE_CALL = 1; // 0x1 - field public static final int MOBILE_DATA_POLICY_MMS_ALWAYS_ALLOWED = 2; // 0x2 field public static final int UNKNOWN_CARRIER_ID_LIST_VERSION = -1; // 0xffffffff } @@ -4356,835 +1793,18 @@ package android.telephony.emergency { package android.telephony.ims { - public final class ImsCallForwardInfo implements android.os.Parcelable { - ctor public ImsCallForwardInfo(int, int, int, int, @NonNull String, int); - method public int describeContents(); - method public int getCondition(); - method public String getNumber(); - method public int getServiceClass(); - method public int getStatus(); - method public int getTimeSeconds(); - method public int getToA(); - method public void writeToParcel(android.os.Parcel, int); - field public static final int CDIV_CF_REASON_ALL = 4; // 0x4 - field public static final int CDIV_CF_REASON_ALL_CONDITIONAL = 5; // 0x5 - field public static final int CDIV_CF_REASON_BUSY = 1; // 0x1 - field public static final int CDIV_CF_REASON_NOT_LOGGED_IN = 6; // 0x6 - field public static final int CDIV_CF_REASON_NOT_REACHABLE = 3; // 0x3 - field public static final int CDIV_CF_REASON_NO_REPLY = 2; // 0x2 - field public static final int CDIV_CF_REASON_UNCONDITIONAL = 0; // 0x0 - field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.ImsCallForwardInfo> CREATOR; - field public static final int STATUS_ACTIVE = 1; // 0x1 - field public static final int STATUS_NOT_ACTIVE = 0; // 0x0 - field public static final int TYPE_OF_ADDRESS_INTERNATIONAL = 145; // 0x91 - field public static final int TYPE_OF_ADDRESS_UNKNOWN = 129; // 0x81 - } - public final class ImsCallProfile implements android.os.Parcelable { - ctor public ImsCallProfile(); - ctor public ImsCallProfile(int, int); - ctor public ImsCallProfile(int, int, android.os.Bundle, android.telephony.ims.ImsStreamMediaProfile); - method public int describeContents(); - method public String getCallExtra(String); - method public String getCallExtra(String, String); - method public boolean getCallExtraBoolean(String); - method public boolean getCallExtraBoolean(String, boolean); - method public int getCallExtraInt(String); - method public int getCallExtraInt(String, int); - method public android.os.Bundle getCallExtras(); - method public int getCallType(); - method public static int getCallTypeFromVideoState(int); - method public int getCallerNumberVerificationStatus(); - method public int getEmergencyCallRouting(); - method public int getEmergencyServiceCategories(); - method @NonNull public java.util.List<java.lang.String> getEmergencyUrns(); - method public android.telephony.ims.ImsStreamMediaProfile getMediaProfile(); - method @NonNull public android.os.Bundle getProprietaryCallExtras(); - method public int getRestrictCause(); - method public int getServiceType(); - method public static int getVideoStateFromCallType(int); - method public static int getVideoStateFromImsCallProfile(android.telephony.ims.ImsCallProfile); - method public boolean hasKnownUserIntentEmergency(); - method public boolean isEmergencyCallTesting(); - method public boolean isVideoCall(); - method public boolean isVideoPaused(); - method public static int presentationToOir(int); - method public void setCallExtra(String, String); - method public void setCallExtraBoolean(String, boolean); - method public void setCallExtraInt(String, int); - method public void setCallRestrictCause(int); - method public void setCallerNumberVerificationStatus(int); - method public void setEmergencyCallRouting(int); - method public void setEmergencyCallTesting(boolean); - method public void setEmergencyServiceCategories(int); - method public void setEmergencyUrns(@NonNull java.util.List<java.lang.String>); - method public void setHasKnownUserIntentEmergency(boolean); - method public void updateCallExtras(android.telephony.ims.ImsCallProfile); - method public void updateCallType(android.telephony.ims.ImsCallProfile); - method public void updateMediaProfile(android.telephony.ims.ImsCallProfile); - method public void writeToParcel(android.os.Parcel, int); - field public static final int CALL_RESTRICT_CAUSE_DISABLED = 2; // 0x2 - field public static final int CALL_RESTRICT_CAUSE_HD = 3; // 0x3 - field public static final int CALL_RESTRICT_CAUSE_NONE = 0; // 0x0 - field public static final int CALL_RESTRICT_CAUSE_RAT = 1; // 0x1 - field public static final int CALL_TYPE_VIDEO_N_VOICE = 3; // 0x3 - field public static final int CALL_TYPE_VOICE = 2; // 0x2 - field public static final int CALL_TYPE_VOICE_N_VIDEO = 1; // 0x1 - field public static final int CALL_TYPE_VS = 8; // 0x8 - field public static final int CALL_TYPE_VS_RX = 10; // 0xa - field public static final int CALL_TYPE_VS_TX = 9; // 0x9 - field public static final int CALL_TYPE_VT = 4; // 0x4 - field public static final int CALL_TYPE_VT_NODIR = 7; // 0x7 - field public static final int CALL_TYPE_VT_RX = 6; // 0x6 - field public static final int CALL_TYPE_VT_TX = 5; // 0x5 - field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.ImsCallProfile> CREATOR; - field public static final int DIALSTRING_NORMAL = 0; // 0x0 - field public static final int DIALSTRING_SS_CONF = 1; // 0x1 - field public static final int DIALSTRING_USSD = 2; // 0x2 - field public static final String EXTRA_ADDITIONAL_CALL_INFO = "AdditionalCallInfo"; - field public static final String EXTRA_ADDITIONAL_SIP_INVITE_FIELDS = "android.telephony.ims.extra.ADDITIONAL_SIP_INVITE_FIELDS"; - field public static final String EXTRA_CALL_DISCONNECT_CAUSE = "android.telephony.ims.extra.CALL_DISCONNECT_CAUSE"; - field public static final String EXTRA_CALL_NETWORK_TYPE = "android.telephony.ims.extra.CALL_NETWORK_TYPE"; - field @Deprecated public static final String EXTRA_CALL_RAT_TYPE = "CallRadioTech"; - field public static final String EXTRA_CHILD_NUMBER = "ChildNum"; - field public static final String EXTRA_CNA = "cna"; - field public static final String EXTRA_CNAP = "cnap"; - field public static final String EXTRA_CODEC = "Codec"; - field public static final String EXTRA_DIALSTRING = "dialstring"; - field public static final String EXTRA_DISPLAY_TEXT = "DisplayText"; - field public static final String EXTRA_EMERGENCY_CALL = "e_call"; - field public static final String EXTRA_FORWARDED_NUMBER = "android.telephony.ims.extra.FORWARDED_NUMBER"; - field public static final String EXTRA_IS_CALL_PULL = "CallPull"; field public static final String EXTRA_OEM_EXTRAS = "android.telephony.ims.extra.OEM_EXTRAS"; - field public static final String EXTRA_OI = "oi"; - field public static final String EXTRA_OIR = "oir"; - field public static final String EXTRA_REMOTE_URI = "remote_uri"; - field public static final String EXTRA_USSD = "ussd"; - field public static final int OIR_DEFAULT = 0; // 0x0 - field public static final int OIR_PRESENTATION_NOT_RESTRICTED = 2; // 0x2 - field public static final int OIR_PRESENTATION_PAYPHONE = 4; // 0x4 - field public static final int OIR_PRESENTATION_RESTRICTED = 1; // 0x1 - field public static final int OIR_PRESENTATION_UNKNOWN = 3; // 0x3 - field public static final int SERVICE_TYPE_EMERGENCY = 2; // 0x2 - field public static final int SERVICE_TYPE_NONE = 0; // 0x0 - field public static final int SERVICE_TYPE_NORMAL = 1; // 0x1 - field public static final int VERIFICATION_STATUS_FAILED = 2; // 0x2 - field public static final int VERIFICATION_STATUS_NOT_VERIFIED = 0; // 0x0 - field public static final int VERIFICATION_STATUS_PASSED = 1; // 0x1 - } - - public class ImsCallSessionListener { - method public void callQualityChanged(@NonNull android.telephony.CallQuality); - method public void callSessionConferenceExtendFailed(android.telephony.ims.ImsReasonInfo); - method public void callSessionConferenceExtendReceived(android.telephony.ims.stub.ImsCallSessionImplBase, android.telephony.ims.ImsCallProfile); - method public void callSessionConferenceExtended(android.telephony.ims.stub.ImsCallSessionImplBase, android.telephony.ims.ImsCallProfile); - method public void callSessionConferenceStateUpdated(android.telephony.ims.ImsConferenceState); - method @Deprecated public void callSessionHandover(int, int, android.telephony.ims.ImsReasonInfo); - method @Deprecated public void callSessionHandoverFailed(int, int, android.telephony.ims.ImsReasonInfo); - method public void callSessionHeld(android.telephony.ims.ImsCallProfile); - method public void callSessionHoldFailed(android.telephony.ims.ImsReasonInfo); - method public void callSessionHoldReceived(android.telephony.ims.ImsCallProfile); - method public void callSessionInitiated(android.telephony.ims.ImsCallProfile); - method public void callSessionInitiatedFailed(android.telephony.ims.ImsReasonInfo); - method public void callSessionInviteParticipantsRequestDelivered(); - method public void callSessionInviteParticipantsRequestFailed(android.telephony.ims.ImsReasonInfo); - method @Deprecated public void callSessionMayHandover(int, int); - method public void callSessionMergeComplete(android.telephony.ims.stub.ImsCallSessionImplBase); - method public void callSessionMergeFailed(android.telephony.ims.ImsReasonInfo); - method public void callSessionMergeStarted(android.telephony.ims.stub.ImsCallSessionImplBase, android.telephony.ims.ImsCallProfile); - method public void callSessionMultipartyStateChanged(boolean); - method public void callSessionProgressing(android.telephony.ims.ImsStreamMediaProfile); - method public void callSessionRemoveParticipantsRequestDelivered(); - method public void callSessionRemoveParticipantsRequestFailed(android.telephony.ims.ImsReasonInfo); - method public void callSessionResumeFailed(android.telephony.ims.ImsReasonInfo); - method public void callSessionResumeReceived(android.telephony.ims.ImsCallProfile); - method public void callSessionResumed(android.telephony.ims.ImsCallProfile); - method public void callSessionRttAudioIndicatorChanged(@NonNull android.telephony.ims.ImsStreamMediaProfile); - method public void callSessionRttMessageReceived(String); - method public void callSessionRttModifyRequestReceived(android.telephony.ims.ImsCallProfile); - method public void callSessionRttModifyResponseReceived(int); - method public void callSessionSuppServiceReceived(android.telephony.ims.ImsSuppServiceNotification); - method public void callSessionTerminated(android.telephony.ims.ImsReasonInfo); - method public void callSessionTtyModeReceived(int); - method public void callSessionUpdateFailed(android.telephony.ims.ImsReasonInfo); - method public void callSessionUpdateReceived(android.telephony.ims.ImsCallProfile); - method public void callSessionUpdated(android.telephony.ims.ImsCallProfile); - method public void callSessionUssdMessageReceived(int, String); - method public void onHandover(int, int, @Nullable android.telephony.ims.ImsReasonInfo); - method public void onHandoverFailed(int, int, @NonNull android.telephony.ims.ImsReasonInfo); - method public void onMayHandover(int, int); - } - - public final class ImsConferenceState implements android.os.Parcelable { - method public int describeContents(); - method public static int getConnectionStateForStatus(String); - method public void writeToParcel(android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.ImsConferenceState> CREATOR; - field public static final String DISPLAY_TEXT = "display-text"; - field public static final String ENDPOINT = "endpoint"; - field public static final String SIP_STATUS_CODE = "sipstatuscode"; - field public static final String STATUS = "status"; - field public static final String STATUS_ALERTING = "alerting"; - field public static final String STATUS_CONNECTED = "connected"; - field public static final String STATUS_CONNECT_FAIL = "connect-fail"; - field public static final String STATUS_DIALING_IN = "dialing-in"; - field public static final String STATUS_DIALING_OUT = "dialing-out"; - field public static final String STATUS_DISCONNECTED = "disconnected"; - field public static final String STATUS_DISCONNECTING = "disconnecting"; - field public static final String STATUS_MUTED_VIA_FOCUS = "muted-via-focus"; - field public static final String STATUS_ON_HOLD = "on-hold"; - field public static final String STATUS_PENDING = "pending"; - field public static final String STATUS_SEND_ONLY = "sendonly"; - field public static final String STATUS_SEND_RECV = "sendrecv"; - field public static final String USER = "user"; - field public final java.util.HashMap<java.lang.String,android.os.Bundle> mParticipants; - } - - public final class ImsException extends java.lang.Exception { - ctor public ImsException(@Nullable String); - ctor public ImsException(@Nullable String, int); - ctor public ImsException(@Nullable String, int, @Nullable Throwable); - } - - public final class ImsExternalCallState implements android.os.Parcelable { - ctor public ImsExternalCallState(@NonNull String, @NonNull android.net.Uri, @Nullable android.net.Uri, boolean, int, int, boolean); - method public int describeContents(); - method @NonNull public android.net.Uri getAddress(); - method public int getCallId(); - method public int getCallState(); - method public int getCallType(); - method @Nullable public android.net.Uri getLocalAddress(); - method public boolean isCallHeld(); - method public boolean isCallPullable(); - method public void writeToParcel(android.os.Parcel, int); - field public static final int CALL_STATE_CONFIRMED = 1; // 0x1 - field public static final int CALL_STATE_TERMINATED = 2; // 0x2 - field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.ImsExternalCallState> CREATOR; - } - - public class ImsMmTelManager implements android.telephony.ims.RegistrationManager { - method @Deprecated @NonNull @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public static android.telephony.ims.ImsMmTelManager createForSubscriptionId(int); - method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void getFeatureState(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>) throws android.telephony.ims.ImsException; - method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void getRegistrationState(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>); - method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getVoWiFiRoamingModeSetting(); - method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isAvailable(int, int); - method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isCapable(int, int); - method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void isSupported(int, int, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>) throws android.telephony.ims.ImsException; - method @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void registerImsRegistrationCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.ImsMmTelManager.RegistrationCallback) throws android.telephony.ims.ImsException; - method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setAdvancedCallingSettingEnabled(boolean); - method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setRttCapabilitySetting(boolean); - method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setVoWiFiModeSetting(int); - method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setVoWiFiNonPersistent(boolean, int); - method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setVoWiFiRoamingModeSetting(int); - method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setVoWiFiRoamingSettingEnabled(boolean); - method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setVoWiFiSettingEnabled(boolean); - method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setVtSettingEnabled(boolean); - method @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void unregisterImsRegistrationCallback(@NonNull android.telephony.ims.ImsMmTelManager.RegistrationCallback); - } - - @Deprecated public static class ImsMmTelManager.RegistrationCallback extends android.telephony.ims.RegistrationManager.RegistrationCallback { - ctor @Deprecated public ImsMmTelManager.RegistrationCallback(); - } - - public class ImsService extends android.app.Service { - ctor public ImsService(); - method public android.telephony.ims.feature.MmTelFeature createMmTelFeature(int); - method public android.telephony.ims.feature.RcsFeature createRcsFeature(int); - method public void disableIms(int); - method public void enableIms(int); - method public android.telephony.ims.stub.ImsConfigImplBase getConfig(int); - method public android.telephony.ims.stub.ImsRegistrationImplBase getRegistration(int); - method public final void onUpdateSupportedImsFeatures(android.telephony.ims.stub.ImsFeatureConfiguration) throws android.os.RemoteException; - method public android.telephony.ims.stub.ImsFeatureConfiguration querySupportedImsFeatures(); - method public void readyForFeatureCreation(); - } - - public final class ImsSsData implements android.os.Parcelable { - ctor public ImsSsData(int, int, int, int, int); - method public int describeContents(); - method @Nullable public java.util.List<android.telephony.ims.ImsCallForwardInfo> getCallForwardInfo(); - method public int getRequestType(); - method public int getResult(); - method public int getServiceClass(); - method public int getServiceType(); - method @NonNull public java.util.List<android.telephony.ims.ImsSsInfo> getSuppServiceInfo(); - method public int getTeleserviceType(); - method public boolean isTypeBarring(); - method public boolean isTypeCf(); - method public boolean isTypeClip(); - method public boolean isTypeClir(); - method public boolean isTypeColp(); - method public boolean isTypeColr(); - method public boolean isTypeCw(); - method public boolean isTypeIcb(); - method public boolean isTypeInterrogation(); - method public boolean isTypeUnConditional(); - method public void writeToParcel(android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.ImsSsData> CREATOR; - field public static final int RESULT_SUCCESS = 0; // 0x0 - field public static final int SERVICE_CLASS_DATA = 2; // 0x2 - field public static final int SERVICE_CLASS_DATA_CIRCUIT_ASYNC = 32; // 0x20 - field public static final int SERVICE_CLASS_DATA_CIRCUIT_SYNC = 16; // 0x10 - field public static final int SERVICE_CLASS_DATA_PACKET_ACCESS = 64; // 0x40 - field public static final int SERVICE_CLASS_DATA_PAD = 128; // 0x80 - field public static final int SERVICE_CLASS_FAX = 4; // 0x4 - field public static final int SERVICE_CLASS_NONE = 0; // 0x0 - field public static final int SERVICE_CLASS_SMS = 8; // 0x8 - field public static final int SERVICE_CLASS_VOICE = 1; // 0x1 - field public static final int SS_ACTIVATION = 0; // 0x0 - field public static final int SS_ALL_BARRING = 18; // 0x12 - field public static final int SS_ALL_DATA_TELESERVICES = 3; // 0x3 - field public static final int SS_ALL_TELESERVICES_EXCEPT_SMS = 5; // 0x5 - field public static final int SS_ALL_TELESEVICES = 1; // 0x1 - field public static final int SS_ALL_TELE_AND_BEARER_SERVICES = 0; // 0x0 - field public static final int SS_BAIC = 16; // 0x10 - field public static final int SS_BAIC_ROAMING = 17; // 0x11 - field public static final int SS_BAOC = 13; // 0xd - field public static final int SS_BAOIC = 14; // 0xe - field public static final int SS_BAOIC_EXC_HOME = 15; // 0xf - field public static final int SS_CFU = 0; // 0x0 - field public static final int SS_CFUT = 6; // 0x6 - field public static final int SS_CF_ALL = 4; // 0x4 - field public static final int SS_CF_ALL_CONDITIONAL = 5; // 0x5 - field public static final int SS_CF_BUSY = 1; // 0x1 - field public static final int SS_CF_NOT_REACHABLE = 3; // 0x3 - field public static final int SS_CF_NO_REPLY = 2; // 0x2 - field public static final int SS_CLIP = 7; // 0x7 - field public static final int SS_CLIR = 8; // 0x8 - field public static final int SS_CNAP = 11; // 0xb - field public static final int SS_COLP = 9; // 0x9 - field public static final int SS_COLR = 10; // 0xa - field public static final int SS_DEACTIVATION = 1; // 0x1 - field public static final int SS_ERASURE = 4; // 0x4 - field public static final int SS_INCOMING_BARRING = 20; // 0x14 - field public static final int SS_INCOMING_BARRING_ANONYMOUS = 22; // 0x16 - field public static final int SS_INCOMING_BARRING_DN = 21; // 0x15 - field public static final int SS_INTERROGATION = 2; // 0x2 - field public static final int SS_OUTGOING_BARRING = 19; // 0x13 - field public static final int SS_REGISTRATION = 3; // 0x3 - field public static final int SS_SMS_SERVICES = 4; // 0x4 - field public static final int SS_TELEPHONY = 2; // 0x2 - field public static final int SS_WAIT = 12; // 0xc - } - - public static final class ImsSsData.Builder { - ctor public ImsSsData.Builder(int, int, int, int, int); - method @NonNull public android.telephony.ims.ImsSsData build(); - method @NonNull public android.telephony.ims.ImsSsData.Builder setCallForwardingInfo(@NonNull java.util.List<android.telephony.ims.ImsCallForwardInfo>); - method @NonNull public android.telephony.ims.ImsSsData.Builder setSuppServiceInfo(@NonNull java.util.List<android.telephony.ims.ImsSsInfo>); - } - - public final class ImsSsInfo implements android.os.Parcelable { - ctor @Deprecated public ImsSsInfo(int, @Nullable String); - method public int describeContents(); - method public int getClirInterrogationStatus(); - method public int getClirOutgoingState(); - method @Deprecated public String getIcbNum(); - method @Nullable public String getIncomingCommunicationBarringNumber(); - method public int getProvisionStatus(); - method public int getStatus(); - method public void writeToParcel(android.os.Parcel, int); - field public static final int CLIR_OUTGOING_DEFAULT = 0; // 0x0 - field public static final int CLIR_OUTGOING_INVOCATION = 1; // 0x1 - field public static final int CLIR_OUTGOING_SUPPRESSION = 2; // 0x2 - field public static final int CLIR_STATUS_NOT_PROVISIONED = 0; // 0x0 - field public static final int CLIR_STATUS_PROVISIONED_PERMANENT = 1; // 0x1 - field public static final int CLIR_STATUS_TEMPORARILY_ALLOWED = 4; // 0x4 - field public static final int CLIR_STATUS_TEMPORARILY_RESTRICTED = 3; // 0x3 - field public static final int CLIR_STATUS_UNKNOWN = 2; // 0x2 - field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.ImsSsInfo> CREATOR; - field public static final int DISABLED = 0; // 0x0 - field public static final int ENABLED = 1; // 0x1 - field public static final int NOT_REGISTERED = -1; // 0xffffffff - field public static final int SERVICE_NOT_PROVISIONED = 0; // 0x0 - field public static final int SERVICE_PROVISIONED = 1; // 0x1 - field public static final int SERVICE_PROVISIONING_UNKNOWN = -1; // 0xffffffff - } - - public static final class ImsSsInfo.Builder { - ctor public ImsSsInfo.Builder(int); - method @NonNull public android.telephony.ims.ImsSsInfo build(); - method @NonNull public android.telephony.ims.ImsSsInfo.Builder setClirInterrogationStatus(int); - method @NonNull public android.telephony.ims.ImsSsInfo.Builder setClirOutgoingState(int); - method @NonNull public android.telephony.ims.ImsSsInfo.Builder setIncomingCommunicationBarringNumber(@NonNull String); - method @NonNull public android.telephony.ims.ImsSsInfo.Builder setProvisionStatus(int); - } - - public final class ImsStreamMediaProfile implements android.os.Parcelable { - ctor public ImsStreamMediaProfile(int, int, int, int, int); - method public void copyFrom(android.telephony.ims.ImsStreamMediaProfile); - method public int describeContents(); - method public int getAudioDirection(); - method public int getAudioQuality(); - method public int getRttMode(); - method public int getVideoDirection(); - method public int getVideoQuality(); - method public boolean isReceivingRttAudio(); - method public boolean isRttCall(); - method public void setReceivingRttAudio(boolean); - method public void setRttMode(int); - method public void writeToParcel(android.os.Parcel, int); - field public static final int AUDIO_QUALITY_AMR = 1; // 0x1 - field public static final int AUDIO_QUALITY_AMR_WB = 2; // 0x2 - field public static final int AUDIO_QUALITY_EVRC = 4; // 0x4 - field public static final int AUDIO_QUALITY_EVRC_B = 5; // 0x5 - field public static final int AUDIO_QUALITY_EVRC_NW = 7; // 0x7 - field public static final int AUDIO_QUALITY_EVRC_WB = 6; // 0x6 - field public static final int AUDIO_QUALITY_EVS_FB = 20; // 0x14 - field public static final int AUDIO_QUALITY_EVS_NB = 17; // 0x11 - field public static final int AUDIO_QUALITY_EVS_SWB = 19; // 0x13 - field public static final int AUDIO_QUALITY_EVS_WB = 18; // 0x12 - field public static final int AUDIO_QUALITY_G711A = 13; // 0xd - field public static final int AUDIO_QUALITY_G711AB = 15; // 0xf - field public static final int AUDIO_QUALITY_G711U = 11; // 0xb - field public static final int AUDIO_QUALITY_G722 = 14; // 0xe - field public static final int AUDIO_QUALITY_G723 = 12; // 0xc - field public static final int AUDIO_QUALITY_G729 = 16; // 0x10 - field public static final int AUDIO_QUALITY_GSM_EFR = 8; // 0x8 - field public static final int AUDIO_QUALITY_GSM_FR = 9; // 0x9 - field public static final int AUDIO_QUALITY_GSM_HR = 10; // 0xa - field public static final int AUDIO_QUALITY_NONE = 0; // 0x0 - field public static final int AUDIO_QUALITY_QCELP13K = 3; // 0x3 - field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.ImsStreamMediaProfile> CREATOR; - field public static final int DIRECTION_INACTIVE = 0; // 0x0 - field public static final int DIRECTION_INVALID = -1; // 0xffffffff - field public static final int DIRECTION_RECEIVE = 1; // 0x1 - field public static final int DIRECTION_SEND = 2; // 0x2 - field public static final int DIRECTION_SEND_RECEIVE = 3; // 0x3 - field public static final int RTT_MODE_DISABLED = 0; // 0x0 - field public static final int RTT_MODE_FULL = 1; // 0x1 - field public static final int VIDEO_QUALITY_NONE = 0; // 0x0 - field public static final int VIDEO_QUALITY_QCIF = 1; // 0x1 - field public static final int VIDEO_QUALITY_QVGA_LANDSCAPE = 2; // 0x2 - field public static final int VIDEO_QUALITY_QVGA_PORTRAIT = 4; // 0x4 - field public static final int VIDEO_QUALITY_VGA_LANDSCAPE = 8; // 0x8 - field public static final int VIDEO_QUALITY_VGA_PORTRAIT = 16; // 0x10 - } - - public final class ImsSuppServiceNotification implements android.os.Parcelable { - ctor public ImsSuppServiceNotification(int, int, int, int, String, String[]); - method public int describeContents(); - method public void writeToParcel(android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.ImsSuppServiceNotification> CREATOR; - field public final int code; - field public final String[] history; - field public final int index; - field public final int notificationType; - field public final String number; - field public final int type; - } - - public class ImsUtListener { - method public void onLineIdentificationSupplementaryServiceResponse(int, @NonNull android.telephony.ims.ImsSsInfo); - method public void onSupplementaryServiceIndication(android.telephony.ims.ImsSsData); - method public void onUtConfigurationCallBarringQueried(int, android.telephony.ims.ImsSsInfo[]); - method public void onUtConfigurationCallForwardQueried(int, android.telephony.ims.ImsCallForwardInfo[]); - method public void onUtConfigurationCallWaitingQueried(int, android.telephony.ims.ImsSsInfo[]); - method @Deprecated public void onUtConfigurationQueried(int, android.os.Bundle); - method public void onUtConfigurationQueryFailed(int, android.telephony.ims.ImsReasonInfo); - method public void onUtConfigurationUpdateFailed(int, android.telephony.ims.ImsReasonInfo); - method public void onUtConfigurationUpdated(int); - field @Deprecated public static final String BUNDLE_KEY_CLIR = "queryClir"; - field @Deprecated public static final String BUNDLE_KEY_SSINFO = "imsSsInfo"; - } - - public abstract class ImsVideoCallProvider { - ctor public ImsVideoCallProvider(); - method public void changeCallDataUsage(long); - method public void changeCameraCapabilities(android.telecom.VideoProfile.CameraCapabilities); - method public void changePeerDimensions(int, int); - method public void changeVideoQuality(int); - method public void handleCallSessionEvent(int); - method public abstract void onRequestCallDataUsage(); - method public abstract void onRequestCameraCapabilities(); - method public abstract void onSendSessionModifyRequest(android.telecom.VideoProfile, android.telecom.VideoProfile); - method public abstract void onSendSessionModifyResponse(android.telecom.VideoProfile); - method public abstract void onSetCamera(String); - method public void onSetCamera(String, int); - method public abstract void onSetDeviceOrientation(int); - method public abstract void onSetDisplaySurface(android.view.Surface); - method public abstract void onSetPauseImage(android.net.Uri); - method public abstract void onSetPreviewSurface(android.view.Surface); - method public abstract void onSetZoom(float); - method public void receiveSessionModifyRequest(android.telecom.VideoProfile); - method public void receiveSessionModifyResponse(int, android.telecom.VideoProfile, android.telecom.VideoProfile); - } - - public class ProvisioningManager { - method @NonNull public static android.telephony.ims.ProvisioningManager createForSubscriptionId(int); - method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) @WorkerThread public int getProvisioningIntValue(int); - method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) @WorkerThread public boolean getProvisioningStatusForCapability(int, int); - method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) @WorkerThread public String getProvisioningStringValue(int); - method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) @WorkerThread public boolean getRcsProvisioningStatusForCapability(int); - method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void notifyRcsAutoConfigurationReceived(@NonNull byte[], boolean); - method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void registerProvisioningChangedCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.ProvisioningManager.Callback) throws android.telephony.ims.ImsException; - method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public int setProvisioningIntValue(int, int); - method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public void setProvisioningStatusForCapability(int, int, boolean); - method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public int setProvisioningStringValue(int, @NonNull String); - method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public void setRcsProvisioningStatusForCapability(int, boolean); - method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void unregisterProvisioningChangedCallback(@NonNull android.telephony.ims.ProvisioningManager.Callback); - field public static final int KEY_VOICE_OVER_WIFI_ENTITLEMENT_ID = 67; // 0x43 - field public static final int KEY_VOICE_OVER_WIFI_MODE_OVERRIDE = 27; // 0x1b - field public static final int KEY_VOICE_OVER_WIFI_ROAMING_ENABLED_OVERRIDE = 26; // 0x1a - field public static final int PROVISIONING_VALUE_DISABLED = 0; // 0x0 - field public static final int PROVISIONING_VALUE_ENABLED = 1; // 0x1 - field public static final String STRING_QUERY_RESULT_ERROR_GENERIC = "STRING_QUERY_RESULT_ERROR_GENERIC"; - field public static final String STRING_QUERY_RESULT_ERROR_NOT_READY = "STRING_QUERY_RESULT_ERROR_NOT_READY"; - } - - public static class ProvisioningManager.Callback { - ctor public ProvisioningManager.Callback(); - method public void onProvisioningIntChanged(int, int); - method public void onProvisioningStringChanged(int, @NonNull String); - } - - public class RcsUceAdapter { - method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setUceSettingEnabled(boolean) throws android.telephony.ims.ImsException; } } package android.telephony.ims.feature { - public final class CapabilityChangeRequest implements android.os.Parcelable { - method public void addCapabilitiesToDisableForTech(int, int); - method public void addCapabilitiesToEnableForTech(int, int); - method public int describeContents(); - method public java.util.List<android.telephony.ims.feature.CapabilityChangeRequest.CapabilityPair> getCapabilitiesToDisable(); - method public java.util.List<android.telephony.ims.feature.CapabilityChangeRequest.CapabilityPair> getCapabilitiesToEnable(); - method public void writeToParcel(android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.feature.CapabilityChangeRequest> CREATOR; - } - - public static class CapabilityChangeRequest.CapabilityPair { - ctor public CapabilityChangeRequest.CapabilityPair(int, int); - method public int getCapability(); - method public int getRadioTech(); - } - - public abstract class ImsFeature { - ctor public ImsFeature(); - method public abstract void changeEnabledCapabilities(android.telephony.ims.feature.CapabilityChangeRequest, android.telephony.ims.feature.ImsFeature.CapabilityCallbackProxy); - method public int getFeatureState(); - method public final int getSlotIndex(); - method public abstract void onFeatureReady(); - method public abstract void onFeatureRemoved(); - method public final void setFeatureState(int); - field public static final int CAPABILITY_ERROR_GENERIC = -1; // 0xffffffff - field public static final int CAPABILITY_SUCCESS = 0; // 0x0 - field public static final int FEATURE_EMERGENCY_MMTEL = 0; // 0x0 - field public static final int FEATURE_MMTEL = 1; // 0x1 - field public static final int FEATURE_RCS = 2; // 0x2 - field public static final int STATE_INITIALIZING = 1; // 0x1 - field public static final int STATE_READY = 2; // 0x2 - field public static final int STATE_UNAVAILABLE = 0; // 0x0 - } - @Deprecated public static class ImsFeature.Capabilities { field @Deprecated protected int mCapabilities; } - protected static class ImsFeature.CapabilityCallbackProxy { - method public void onChangeCapabilityConfigurationError(int, int, int); - } - - public class MmTelFeature extends android.telephony.ims.feature.ImsFeature { - ctor public MmTelFeature(); - method public void changeEnabledCapabilities(@NonNull android.telephony.ims.feature.CapabilityChangeRequest, @NonNull android.telephony.ims.feature.ImsFeature.CapabilityCallbackProxy); - method @Nullable public android.telephony.ims.ImsCallProfile createCallProfile(int, int); - method @Nullable public android.telephony.ims.stub.ImsCallSessionImplBase createCallSession(@NonNull android.telephony.ims.ImsCallProfile); - method @NonNull public android.telephony.ims.stub.ImsEcbmImplBase getEcbm(); - method @NonNull public android.telephony.ims.stub.ImsMultiEndpointImplBase getMultiEndpoint(); - method @NonNull public android.telephony.ims.stub.ImsSmsImplBase getSmsImplementation(); - method @NonNull public android.telephony.ims.stub.ImsUtImplBase getUt(); - method public final void notifyCapabilitiesStatusChanged(@NonNull android.telephony.ims.feature.MmTelFeature.MmTelCapabilities); - method public final void notifyIncomingCall(@NonNull android.telephony.ims.stub.ImsCallSessionImplBase, @NonNull android.os.Bundle); - method public final void notifyRejectedCall(@NonNull android.telephony.ims.ImsCallProfile, @NonNull android.telephony.ims.ImsReasonInfo); - method public final void notifyVoiceMessageCountUpdate(int); - method public void onFeatureReady(); - method public void onFeatureRemoved(); - method public boolean queryCapabilityConfiguration(int, int); - method @NonNull public final android.telephony.ims.feature.MmTelFeature.MmTelCapabilities queryCapabilityStatus(); - method public void setUiTtyMode(int, @Nullable android.os.Message); - method public int shouldProcessCall(@NonNull String[]); - field public static final String EXTRA_IS_UNKNOWN_CALL = "android.telephony.ims.feature.extra.IS_UNKNOWN_CALL"; - field public static final String EXTRA_IS_USSD = "android.telephony.ims.feature.extra.IS_USSD"; - field public static final int PROCESS_CALL_CSFB = 1; // 0x1 - field public static final int PROCESS_CALL_IMS = 0; // 0x0 - } - - public static class MmTelFeature.MmTelCapabilities extends android.telephony.ims.feature.ImsFeature.Capabilities { - ctor public MmTelFeature.MmTelCapabilities(); - ctor @Deprecated public MmTelFeature.MmTelCapabilities(android.telephony.ims.feature.ImsFeature.Capabilities); - ctor public MmTelFeature.MmTelCapabilities(int); - method public final void addCapabilities(int); - method public final void removeCapabilities(int); - } - - public class RcsFeature extends android.telephony.ims.feature.ImsFeature { - ctor public RcsFeature(); - method public void changeEnabledCapabilities(@NonNull android.telephony.ims.feature.CapabilityChangeRequest, @NonNull android.telephony.ims.feature.ImsFeature.CapabilityCallbackProxy); - method public void onFeatureReady(); - method public void onFeatureRemoved(); - } - -} - -package android.telephony.ims.stub { - - public class ImsCallSessionImplBase implements java.lang.AutoCloseable { - ctor public ImsCallSessionImplBase(); - method public void accept(int, android.telephony.ims.ImsStreamMediaProfile); - method public void close(); - method public void deflect(String); - method public void extendToConference(String[]); - method public String getCallId(); - method public android.telephony.ims.ImsCallProfile getCallProfile(); - method public android.telephony.ims.ImsVideoCallProvider getImsVideoCallProvider(); - method public android.telephony.ims.ImsCallProfile getLocalCallProfile(); - method public String getProperty(String); - method public android.telephony.ims.ImsCallProfile getRemoteCallProfile(); - method public int getState(); - method public void hold(android.telephony.ims.ImsStreamMediaProfile); - method public void inviteParticipants(String[]); - method public boolean isInCall(); - method public boolean isMultiparty(); - method public void merge(); - method public void reject(int); - method public void removeParticipants(String[]); - method public void resume(android.telephony.ims.ImsStreamMediaProfile); - method public void sendDtmf(char, android.os.Message); - method public void sendRttMessage(String); - method public void sendRttModifyRequest(android.telephony.ims.ImsCallProfile); - method public void sendRttModifyResponse(boolean); - method public void sendUssd(String); - method public void setListener(android.telephony.ims.ImsCallSessionListener); - method public void setMute(boolean); - method public void start(String, android.telephony.ims.ImsCallProfile); - method public void startConference(String[], android.telephony.ims.ImsCallProfile); - method public void startDtmf(char); - method public void stopDtmf(); - method public void terminate(int); - method public void update(int, android.telephony.ims.ImsStreamMediaProfile); - field public static final int USSD_MODE_NOTIFY = 0; // 0x0 - field public static final int USSD_MODE_REQUEST = 1; // 0x1 - } - - public static class ImsCallSessionImplBase.State { - method public static String toString(int); - field public static final int ESTABLISHED = 4; // 0x4 - field public static final int ESTABLISHING = 3; // 0x3 - field public static final int IDLE = 0; // 0x0 - field public static final int INITIATED = 1; // 0x1 - field public static final int INVALID = -1; // 0xffffffff - field public static final int NEGOTIATING = 2; // 0x2 - field public static final int REESTABLISHING = 6; // 0x6 - field public static final int RENEGOTIATING = 5; // 0x5 - field public static final int TERMINATED = 8; // 0x8 - field public static final int TERMINATING = 7; // 0x7 - } - - public class ImsConfigImplBase { - ctor public ImsConfigImplBase(); - method public int getConfigInt(int); - method public String getConfigString(int); - method public final void notifyProvisionedValueChanged(int, int); - method public final void notifyProvisionedValueChanged(int, String); - method public void notifyRcsAutoConfigurationReceived(@NonNull byte[], boolean); - method public int setConfig(int, int); - method public int setConfig(int, String); - field public static final int CONFIG_RESULT_FAILED = 1; // 0x1 - field public static final int CONFIG_RESULT_SUCCESS = 0; // 0x0 - field public static final int CONFIG_RESULT_UNKNOWN = -1; // 0xffffffff - } - - public class ImsEcbmImplBase { - ctor public ImsEcbmImplBase(); - method public final void enteredEcbm(); - method public void exitEmergencyCallbackMode(); - method public final void exitedEcbm(); - } - - public final class ImsFeatureConfiguration implements android.os.Parcelable { - method public int describeContents(); - method public java.util.Set<android.telephony.ims.stub.ImsFeatureConfiguration.FeatureSlotPair> getServiceFeatures(); - method public void writeToParcel(android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.stub.ImsFeatureConfiguration> CREATOR; - } - - public static class ImsFeatureConfiguration.Builder { - ctor public ImsFeatureConfiguration.Builder(); - method public android.telephony.ims.stub.ImsFeatureConfiguration.Builder addFeature(int, int); - method public android.telephony.ims.stub.ImsFeatureConfiguration build(); - } - - public static final class ImsFeatureConfiguration.FeatureSlotPair { - ctor public ImsFeatureConfiguration.FeatureSlotPair(int, int); - field public final int featureType; - field public final int slotId; - } - - public class ImsMultiEndpointImplBase { - ctor public ImsMultiEndpointImplBase(); - method public final void onImsExternalCallStateUpdate(java.util.List<android.telephony.ims.ImsExternalCallState>); - method public void requestImsExternalCallStateInfo(); - } - - public class ImsRegistrationImplBase { - ctor public ImsRegistrationImplBase(); - method public final void onDeregistered(android.telephony.ims.ImsReasonInfo); - method public final void onRegistered(int); - method public final void onRegistering(int); - method public final void onSubscriberAssociatedUriChanged(android.net.Uri[]); - method public final void onTechnologyChangeFailed(int, android.telephony.ims.ImsReasonInfo); - field public static final int REGISTRATION_TECH_IWLAN = 1; // 0x1 - field public static final int REGISTRATION_TECH_LTE = 0; // 0x0 - field public static final int REGISTRATION_TECH_NONE = -1; // 0xffffffff - } - - public class ImsSmsImplBase { - ctor public ImsSmsImplBase(); - method public void acknowledgeSms(int, @IntRange(from=0, to=65535) int, int); - method public void acknowledgeSmsReport(int, @IntRange(from=0, to=65535) int, int); - method public String getSmsFormat(); - method public void onReady(); - method @Deprecated public final void onSendSmsResult(int, @IntRange(from=0, to=65535) int, int, int) throws java.lang.RuntimeException; - method public final void onSendSmsResultError(int, @IntRange(from=0, to=65535) int, int, int, int) throws java.lang.RuntimeException; - method public final void onSendSmsResultSuccess(int, @IntRange(from=0, to=65535) int) throws java.lang.RuntimeException; - method public final void onSmsReceived(int, String, byte[]) throws java.lang.RuntimeException; - method @Deprecated public final void onSmsStatusReportReceived(int, @IntRange(from=0, to=65535) int, String, byte[]) throws java.lang.RuntimeException; - method public final void onSmsStatusReportReceived(int, String, byte[]) throws java.lang.RuntimeException; - method public void sendSms(int, @IntRange(from=0, to=65535) int, String, String, boolean, byte[]); - field public static final int DELIVER_STATUS_ERROR_GENERIC = 2; // 0x2 - field public static final int DELIVER_STATUS_ERROR_NO_MEMORY = 3; // 0x3 - field public static final int DELIVER_STATUS_ERROR_REQUEST_NOT_SUPPORTED = 4; // 0x4 - field public static final int DELIVER_STATUS_OK = 1; // 0x1 - field public static final int RESULT_NO_NETWORK_ERROR = -1; // 0xffffffff - field public static final int SEND_STATUS_ERROR = 2; // 0x2 - field public static final int SEND_STATUS_ERROR_FALLBACK = 4; // 0x4 - field public static final int SEND_STATUS_ERROR_RETRY = 3; // 0x3 - field public static final int SEND_STATUS_OK = 1; // 0x1 - field public static final int STATUS_REPORT_STATUS_ERROR = 2; // 0x2 - field public static final int STATUS_REPORT_STATUS_OK = 1; // 0x1 - } - - public class ImsUtImplBase { - ctor public ImsUtImplBase(); - method public void close(); - method public int queryCallBarring(int); - method public int queryCallBarringForServiceClass(int, int); - method public int queryCallForward(int, String); - method public int queryCallWaiting(); - method public int queryClip(); - method public int queryClir(); - method public int queryColp(); - method public int queryColr(); - method public void setListener(android.telephony.ims.ImsUtListener); - method public int transact(android.os.Bundle); - method public int updateCallBarring(int, int, String[]); - method public int updateCallBarringForServiceClass(int, int, String[], int); - method public int updateCallForward(int, int, String, int, int); - method public int updateCallWaiting(boolean, int); - method public int updateClip(boolean); - method public int updateClir(int); - method public int updateColp(boolean); - method public int updateColr(int); - } - -} - -package android.telephony.mbms { - - public static class DownloadRequest.Builder { - method public android.telephony.mbms.DownloadRequest.Builder setServiceId(String); - } - - public final class FileInfo implements android.os.Parcelable { - ctor public FileInfo(android.net.Uri, String); - } - - public final class FileServiceInfo extends android.telephony.mbms.ServiceInfo implements android.os.Parcelable { - ctor public FileServiceInfo(java.util.Map<java.util.Locale,java.lang.String>, String, java.util.List<java.util.Locale>, String, java.util.Date, java.util.Date, java.util.List<android.telephony.mbms.FileInfo>); - } - - public final class StreamingServiceInfo extends android.telephony.mbms.ServiceInfo implements android.os.Parcelable { - ctor public StreamingServiceInfo(java.util.Map<java.util.Locale,java.lang.String>, String, java.util.List<java.util.Locale>, String, java.util.Date, java.util.Date); - } - - public final class UriPathPair implements android.os.Parcelable { - method public int describeContents(); - method public android.net.Uri getContentUri(); - method public android.net.Uri getFilePathUri(); - method public void writeToParcel(android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.telephony.mbms.UriPathPair> CREATOR; - } - -} - -package android.telephony.mbms.vendor { - - public class MbmsDownloadServiceBase extends android.os.Binder implements android.os.IInterface { - ctor public MbmsDownloadServiceBase(); - method public int addProgressListener(android.telephony.mbms.DownloadRequest, android.telephony.mbms.DownloadProgressListener) throws android.os.RemoteException; - method public int addServiceAnnouncement(int, @NonNull byte[]); - method public int addStatusListener(android.telephony.mbms.DownloadRequest, android.telephony.mbms.DownloadStatusListener) throws android.os.RemoteException; - method public android.os.IBinder asBinder(); - method public int cancelDownload(android.telephony.mbms.DownloadRequest) throws android.os.RemoteException; - method public void dispose(int) throws android.os.RemoteException; - method public int download(android.telephony.mbms.DownloadRequest) throws android.os.RemoteException; - method public int initialize(int, android.telephony.mbms.MbmsDownloadSessionCallback) throws android.os.RemoteException; - method @NonNull public java.util.List<android.telephony.mbms.DownloadRequest> listPendingDownloads(int) throws android.os.RemoteException; - method public void onAppCallbackDied(int, int); - method public boolean onTransact(int, android.os.Parcel, android.os.Parcel, int) throws android.os.RemoteException; - method public int removeProgressListener(android.telephony.mbms.DownloadRequest, android.telephony.mbms.DownloadProgressListener) throws android.os.RemoteException; - method public int removeStatusListener(android.telephony.mbms.DownloadRequest, android.telephony.mbms.DownloadStatusListener) throws android.os.RemoteException; - method public int requestDownloadState(android.telephony.mbms.DownloadRequest, android.telephony.mbms.FileInfo) throws android.os.RemoteException; - method public int requestUpdateFileServices(int, java.util.List<java.lang.String>) throws android.os.RemoteException; - method public int resetDownloadKnowledge(android.telephony.mbms.DownloadRequest) throws android.os.RemoteException; - method public int setTempFileRootDirectory(int, String) throws android.os.RemoteException; - } - - public class MbmsGroupCallServiceBase extends android.app.Service { - ctor public MbmsGroupCallServiceBase(); - method public void dispose(int) throws android.os.RemoteException; - method public int initialize(@NonNull android.telephony.mbms.MbmsGroupCallSessionCallback, int) throws android.os.RemoteException; - method public void onAppCallbackDied(int, int); - method public android.os.IBinder onBind(android.content.Intent); - method public int startGroupCall(int, long, @NonNull java.util.List<java.lang.Integer>, @NonNull java.util.List<java.lang.Integer>, @NonNull android.telephony.mbms.GroupCallCallback); - method public void stopGroupCall(int, long); - method public void updateGroupCall(int, long, @NonNull java.util.List<java.lang.Integer>, @NonNull java.util.List<java.lang.Integer>); - } - - public class MbmsStreamingServiceBase extends android.os.Binder implements android.os.IInterface { - ctor public MbmsStreamingServiceBase(); - method public android.os.IBinder asBinder(); - method public void dispose(int) throws android.os.RemoteException; - method @Nullable public android.net.Uri getPlaybackUri(int, String) throws android.os.RemoteException; - method public int initialize(android.telephony.mbms.MbmsStreamingSessionCallback, int) throws android.os.RemoteException; - method public void onAppCallbackDied(int, int); - method public boolean onTransact(int, android.os.Parcel, android.os.Parcel, int) throws android.os.RemoteException; - method public int requestUpdateStreamingServices(int, java.util.List<java.lang.String>) throws android.os.RemoteException; - method public int startStreaming(int, String, android.telephony.mbms.StreamingServiceCallback) throws android.os.RemoteException; - method public void stopStreaming(int, String) throws android.os.RemoteException; - } - - public class VendorUtils { - ctor public VendorUtils(); - method public static android.content.ComponentName getAppReceiverFromPackageName(android.content.Context, String); - field public static final String ACTION_CLEANUP = "android.telephony.mbms.action.CLEANUP"; - field public static final String ACTION_DOWNLOAD_RESULT_INTERNAL = "android.telephony.mbms.action.DOWNLOAD_RESULT_INTERNAL"; - field public static final String ACTION_FILE_DESCRIPTOR_REQUEST = "android.telephony.mbms.action.FILE_DESCRIPTOR_REQUEST"; - field public static final String EXTRA_FD_COUNT = "android.telephony.mbms.extra.FD_COUNT"; - field public static final String EXTRA_FINAL_URI = "android.telephony.mbms.extra.FINAL_URI"; - field public static final String EXTRA_FREE_URI_LIST = "android.telephony.mbms.extra.FREE_URI_LIST"; - field public static final String EXTRA_PAUSED_LIST = "android.telephony.mbms.extra.PAUSED_LIST"; - field public static final String EXTRA_PAUSED_URI_LIST = "android.telephony.mbms.extra.PAUSED_URI_LIST"; - field public static final String EXTRA_SERVICE_ID = "android.telephony.mbms.extra.SERVICE_ID"; - field public static final String EXTRA_TEMP_FILES_IN_USE = "android.telephony.mbms.extra.TEMP_FILES_IN_USE"; - field public static final String EXTRA_TEMP_FILE_ROOT = "android.telephony.mbms.extra.TEMP_FILE_ROOT"; - field public static final String EXTRA_TEMP_LIST = "android.telephony.mbms.extra.TEMP_LIST"; - } - } package android.text { @@ -5223,7 +1843,6 @@ package android.util { field public static final String HEARING_AID_SETTINGS = "settings_bluetooth_hearing_aid"; field public static final String PERSIST_PREFIX = "persist.sys.fflag.override."; field public static final String SCREENRECORD_LONG_PRESS = "settings_screenrecord_long_press"; - field public static final String SEAMLESS_TRANSFER = "settings_seamless_transfer"; field public static final String SETTINGS_WIFITRACKER2 = "settings_wifitracker2"; } @@ -5438,10 +2057,7 @@ package android.view.accessibility { public final class AccessibilityManager { method public void addAccessibilityServicesStateChangeListener(@NonNull android.view.accessibility.AccessibilityManager.AccessibilityServicesStateChangeListener, @Nullable android.os.Handler); method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_ACCESSIBILITY) public java.util.List<java.lang.String> getAccessibilityShortcutTargets(int); - method @RequiresPermission(android.Manifest.permission.MANAGE_ACCESSIBILITY) public void performAccessibilityShortcut(); - method @RequiresPermission(android.Manifest.permission.MANAGE_ACCESSIBILITY) public void registerSystemAction(@NonNull android.app.RemoteAction, int); method public void removeAccessibilityServicesStateChangeListener(@NonNull android.view.accessibility.AccessibilityManager.AccessibilityServicesStateChangeListener); - method @RequiresPermission(android.Manifest.permission.MANAGE_ACCESSIBILITY) public void unregisterSystemAction(int); } public static interface AccessibilityManager.AccessibilityServicesStateChangeListener { @@ -5487,7 +2103,6 @@ package android.view.autofill { } public final class AutofillManager { - method public void setAugmentedAutofillWhitelist(@Nullable java.util.Set<java.lang.String>, @Nullable java.util.Set<android.content.ComponentName>); field public static final String DEVICE_CONFIG_AUTOFILL_SMART_SUGGESTION_SUPPORTED_MODES = "smart_suggestion_supported_modes"; field public static final int FLAG_SMART_SUGGESTION_OFF = 0; // 0x0 field public static final int FLAG_SMART_SUGGESTION_SYSTEM = 1; // 0x1 @@ -5498,42 +2113,7 @@ package android.view.autofill { package android.view.contentcapture { - public final class ContentCaptureContext implements android.os.Parcelable { - method @Nullable public android.content.ComponentName getActivityComponent(); - method public int getDisplayId(); - method public int getFlags(); - method @Nullable public android.view.contentcapture.ContentCaptureSessionId getParentSessionId(); - method public int getTaskId(); - field public static final int FLAG_DISABLED_BY_APP = 1; // 0x1 - field public static final int FLAG_DISABLED_BY_FLAG_SECURE = 2; // 0x2 - field public static final int FLAG_RECONNECTED = 4; // 0x4 - } - - public final class ContentCaptureEvent implements android.os.Parcelable { - method public int describeContents(); - method @Nullable public android.view.contentcapture.ContentCaptureContext getContentCaptureContext(); - method public long getEventTime(); - method @Nullable public android.view.autofill.AutofillId getId(); - method @Nullable public java.util.List<android.view.autofill.AutofillId> getIds(); - method @Nullable public android.graphics.Insets getInsets(); - method @Nullable public CharSequence getText(); - method public int getType(); - method @Nullable public android.view.contentcapture.ViewNode getViewNode(); - method public void writeToParcel(android.os.Parcel, int); - field @NonNull public static final android.os.Parcelable.Creator<android.view.contentcapture.ContentCaptureEvent> CREATOR; - field public static final int TYPE_CONTEXT_UPDATED = 6; // 0x6 - field public static final int TYPE_SESSION_PAUSED = 8; // 0x8 - field public static final int TYPE_SESSION_RESUMED = 7; // 0x7 - field public static final int TYPE_VIEW_APPEARED = 1; // 0x1 - field public static final int TYPE_VIEW_DISAPPEARED = 2; // 0x2 - field public static final int TYPE_VIEW_INSETS_CHANGED = 9; // 0x9 - field public static final int TYPE_VIEW_TEXT_CHANGED = 3; // 0x3 - field public static final int TYPE_VIEW_TREE_APPEARED = 5; // 0x5 - field public static final int TYPE_VIEW_TREE_APPEARING = 4; // 0x4 - } - public final class ContentCaptureManager { - method public boolean isContentCaptureFeatureEnabled(); field public static final String DEVICE_CONFIG_PROPERTY_IDLE_FLUSH_FREQUENCY = "idle_flush_frequency"; field public static final String DEVICE_CONFIG_PROPERTY_LOGGING_LEVEL = "logging_level"; field public static final String DEVICE_CONFIG_PROPERTY_LOG_HISTORY_SIZE = "log_history_size"; @@ -5546,7 +2126,6 @@ package android.view.contentcapture { } public final class ViewNode extends android.app.assist.AssistStructure.ViewNode { - method @Nullable public android.view.autofill.AutofillId getParentAutofillId(); method @Nullable public static android.view.contentcapture.ViewNode readFromParcel(@NonNull android.os.Parcel); method public static void writeToParcel(@NonNull android.os.Parcel, @Nullable android.view.contentcapture.ViewNode, int); } @@ -5784,9 +2363,18 @@ package android.window { field public static final int FEATURE_WINDOW_TOKENS = 2; // 0x2 } + public final class TaskAppearedInfo implements android.os.Parcelable { + ctor public TaskAppearedInfo(@NonNull android.app.ActivityManager.RunningTaskInfo, @NonNull android.view.SurfaceControl); + method public int describeContents(); + method @NonNull public android.view.SurfaceControl getLeash(); + method @NonNull public android.app.ActivityManager.RunningTaskInfo getTaskInfo(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.window.TaskAppearedInfo> CREATOR; + } + public class TaskOrganizer extends android.window.WindowOrganizer { ctor public TaskOrganizer(); - method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public android.app.ActivityManager.RunningTaskInfo createRootTask(int, int); + method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public android.window.TaskAppearedInfo createRootTask(int, int); method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public boolean deleteRootTask(@NonNull android.window.WindowContainerToken); method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public java.util.List<android.app.ActivityManager.RunningTaskInfo> getChildTasks(@NonNull android.window.WindowContainerToken, @NonNull int[]); method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public android.window.WindowContainerToken getImeTarget(int); @@ -5795,10 +2383,10 @@ package android.window { method @BinderThread public void onTaskAppeared(@NonNull android.app.ActivityManager.RunningTaskInfo, @NonNull android.view.SurfaceControl); method @BinderThread public void onTaskInfoChanged(@NonNull android.app.ActivityManager.RunningTaskInfo); method @BinderThread public void onTaskVanished(@NonNull android.app.ActivityManager.RunningTaskInfo); - method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public final void registerOrganizer(); + method @CallSuper @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public java.util.List<android.window.TaskAppearedInfo> registerOrganizer(); method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void setInterceptBackPressedOnTaskRoot(@NonNull android.window.WindowContainerToken, boolean); method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void setLaunchRoot(int, @NonNull android.window.WindowContainerToken); - method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public final void unregisterOrganizer(); + method @CallSuper @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void unregisterOrganizer(); } public final class WindowContainerToken implements android.os.Parcelable { diff --git a/api/test-lint-baseline.txt b/api/test-lint-baseline.txt index a314702b7792..0440d1a95065 100644 --- a/api/test-lint-baseline.txt +++ b/api/test-lint-baseline.txt @@ -2511,6 +2511,8 @@ NoSettingsProvider: android.provider.Settings.Global#DYNAMIC_POWER_SAVINGS_ENABL NoSettingsProvider: android.provider.Settings.Global#HIDDEN_API_BLACKLIST_EXEMPTIONS: +NoSettingsProvider: android.provider.Settings.Global#HIDDEN_API_POLICY: + NoSettingsProvider: android.provider.Settings.Global#HIDE_ERROR_DIALOGS: NoSettingsProvider: android.provider.Settings.Global#LOCATION_GLOBAL_KILL_SWITCH: diff --git a/api/test-removed.txt b/api/test-removed.txt index e47f6edfbff1..d802177e249b 100644 --- a/api/test-removed.txt +++ b/api/test-removed.txt @@ -1,10 +1 @@ // Signature format: 2.0 -package android.app.prediction { - - public static final class AppTarget.Builder { - method @Deprecated @NonNull public android.app.prediction.AppTarget.Builder setTarget(@NonNull String, @NonNull android.os.UserHandle); - method @Deprecated @NonNull public android.app.prediction.AppTarget.Builder setTarget(@NonNull android.content.pm.ShortcutInfo); - } - -} - diff --git a/cmds/idmap2/idmap2d/Idmap2Service.h b/cmds/idmap2/idmap2d/Idmap2Service.h index 1a445192aff8..1d2090c431ae 100644 --- a/cmds/idmap2/idmap2d/Idmap2Service.h +++ b/cmds/idmap2/idmap2d/Idmap2Service.h @@ -22,6 +22,8 @@ #include <android-base/unique_fd.h> #include <binder/BinderService.h> +#include <string> + #include "android/os/BnIdmap2.h" namespace android::os { diff --git a/cmds/idmap2/include/idmap2/BinaryStreamVisitor.h b/cmds/idmap2/include/idmap2/BinaryStreamVisitor.h index ff45b1407dea..bf31cbf8d4f7 100644 --- a/cmds/idmap2/include/idmap2/BinaryStreamVisitor.h +++ b/cmds/idmap2/include/idmap2/BinaryStreamVisitor.h @@ -36,12 +36,11 @@ class BinaryStreamVisitor : public Visitor { void visit(const IdmapData::Header& header) override; private: - void Write(const void* value, size_t length); void Write8(uint8_t value); void Write16(uint16_t value); void Write32(uint32_t value); void WriteString256(const StringPiece& value); - void WriteString(const std::string& value); + void WriteString(const StringPiece& value); std::ostream& stream_; }; diff --git a/cmds/idmap2/include/idmap2/Idmap.h b/cmds/idmap2/include/idmap2/Idmap.h index 0f05592b70f3..a35fad9d686c 100644 --- a/cmds/idmap2/include/idmap2/Idmap.h +++ b/cmds/idmap2/include/idmap2/Idmap.h @@ -17,48 +17,45 @@ /* * # idmap file format (current version) * - * idmap := header data* - * header := magic version target_crc overlay_crc target_path overlay_path debug_info - * data := data_header data_block* - * data_header := target_package_id types_count - * data_block := target_type overlay_type entry_count entry_offset entry* - * overlay_path := string256 - * target_path := string256 - * debug_info := string - * string := <uint32_t> <uint8_t>+ '\0'+ - * entry := <uint32_t> - * entry_count := <uint16_t> - * entry_offset := <uint16_t> - * magic := <uint32_t> - * overlay_crc := <uint32_t> - * overlay_type := <uint16_t> - * string256 := <uint8_t>[256] - * target_crc := <uint32_t> - * target_package_id := <uint16_t> - * target_type := <uint16_t> - * types_count := <uint16_t> - * version := <uint32_t> + * idmap := header data* + * header := magic version target_crc overlay_crc fulfilled_policies + * enforce_overlayable target_path overlay_path debug_info + * data := data_header target_entry* target_inline_entry* overlay_entry* + * string_pool + * data_header := target_package_id overlay_package_id padding(2) target_entry_count + * target_inline_entry_count overlay_entry_count string_pool_index + * target_entry := target_id overlay_id + * target_inline_entry := target_id Res_value::size padding(1) Res_value::type + * Res_value::value + * overlay_entry := overlay_id target_id * - * - * # idmap file format changelog - * ## v1 - * - Identical to idmap v1. - * - * ## v2 - * - Entries are no longer separated by type into type specific data blocks. - * - Added overlay-indexed target resource id lookup capabilities. - * - Target and overlay entries are stored as a sparse array in the data block. The target entries - * array maps from target resource id to overlay data type and value and the array is sorted by - * target resource id. The overlay entries array maps from overlay resource id to target resource - * id and the array is sorted by overlay resource id. It is important for both arrays to be sorted - * to allow for O(log(number_of_overlaid_resources)) performance when looking up resource - * mappings at runtime. - * - Idmap can now encode a type and value to override a resource without needing a table entry. - * - A string pool block is included to retrieve the value of strings that do not have a resource - * table entry. - * - * ## v3 - * - Add 'debug' block to IdmapHeader. + * debug_info := string + * enforce_overlayable := <uint32_t> + * fulfilled_policies := <uint32_t> + * magic := <uint32_t> + * overlay_crc := <uint32_t> + * overlay_entry_count := <uint32_t> + * overlay_id := <uint32_t> + * overlay_package_id := <uint8_t> + * overlay_path := string256 + * padding(n) := <uint8_t>[n] + * Res_value::size := <uint16_t> + * Res_value::type := <uint8_t> + * Res_value::value := <uint32_t> + * string := <uint32_t> <uint8_t>+ padding(n) + * string256 := <uint8_t>[256] + * string_pool := string + * string_pool_index := <uint32_t> + * string_pool_length := <uint32_t> + * target_crc := <uint32_t> + * target_entry_count := <uint32_t> + * target_inline_entry_count := <uint32_t> + * target_id := <uint32_t> + * target_package_id := <uint8_t> + * target_path := string256 + * value_type := <uint8_t> + * value_data := <uint32_t> + * version := <uint32_t> */ #ifndef IDMAP2_INCLUDE_IDMAP2_IDMAP_H_ @@ -183,6 +180,10 @@ class IdmapData { return target_entry_count; } + inline uint32_t GetTargetInlineEntryCount() const { + return target_entry_inline_count; + } + inline uint32_t GetOverlayEntryCount() const { return overlay_entry_count; } @@ -191,19 +192,15 @@ class IdmapData { return string_pool_index_offset; } - inline uint32_t GetStringPoolLength() const { - return string_pool_len; - } - void accept(Visitor* v) const; private: PackageId target_package_id_; PackageId overlay_package_id_; uint32_t target_entry_count; + uint32_t target_entry_inline_count; uint32_t overlay_entry_count; uint32_t string_pool_index_offset; - uint32_t string_pool_len; Header() = default; friend Idmap; @@ -213,8 +210,12 @@ class IdmapData { struct TargetEntry { ResourceId target_id; - TargetValue::DataType data_type; - TargetValue::DataValue data_value; + ResourceId overlay_id; + }; + + struct TargetInlineEntry { + ResourceId target_id; + TargetValue value; }; struct OverlayEntry { @@ -227,20 +228,24 @@ class IdmapData { static Result<std::unique_ptr<const IdmapData>> FromResourceMapping( const ResourceMapping& resource_mapping); - inline const std::unique_ptr<const Header>& GetHeader() const { + const std::unique_ptr<const Header>& GetHeader() const { return header_; } - inline const std::vector<TargetEntry>& GetTargetEntries() const { + const std::vector<TargetEntry>& GetTargetEntries() const { return target_entries_; } - inline const std::vector<OverlayEntry>& GetOverlayEntries() const { + const std::vector<TargetInlineEntry>& GetTargetInlineEntries() const { + return target_inline_entries_; + } + + const std::vector<OverlayEntry>& GetOverlayEntries() const { return overlay_entries_; } - inline const void* GetStringPoolData() const { - return string_pool_.get(); + const std::string& GetStringPoolData() const { + return string_pool_data_; } void accept(Visitor* v) const; @@ -251,8 +256,9 @@ class IdmapData { std::unique_ptr<const Header> header_; std::vector<TargetEntry> target_entries_; + std::vector<TargetInlineEntry> target_inline_entries_; std::vector<OverlayEntry> overlay_entries_; - std::unique_ptr<uint8_t[]> string_pool_; + std::string string_pool_data_; friend Idmap; DISALLOW_COPY_AND_ASSIGN(IdmapData); @@ -304,6 +310,10 @@ class Visitor { virtual void visit(const IdmapData::Header& header) = 0; }; +inline size_t CalculatePadding(size_t data_length) { + return (4 - (data_length % 4)) % 4; +} + } // namespace android::idmap2 #endif // IDMAP2_INCLUDE_IDMAP2_IDMAP_H_ diff --git a/cmds/idmap2/include/idmap2/PrettyPrintVisitor.h b/cmds/idmap2/include/idmap2/PrettyPrintVisitor.h index 5dcf217e2aa3..2b4c76124175 100644 --- a/cmds/idmap2/include/idmap2/PrettyPrintVisitor.h +++ b/cmds/idmap2/include/idmap2/PrettyPrintVisitor.h @@ -41,8 +41,9 @@ class PrettyPrintVisitor : public Visitor { private: std::ostream& stream_; - std::unique_ptr<const ApkAssets> target_apk_; AssetManager2 target_am_; + AssetManager2 overlay_am_; + std::vector<std::unique_ptr<const ApkAssets>> apk_assets_; }; } // namespace idmap2 diff --git a/cmds/idmap2/include/idmap2/RawPrintVisitor.h b/cmds/idmap2/include/idmap2/RawPrintVisitor.h index 92c186453611..58edc99715fd 100644 --- a/cmds/idmap2/include/idmap2/RawPrintVisitor.h +++ b/cmds/idmap2/include/idmap2/RawPrintVisitor.h @@ -45,11 +45,9 @@ class RawPrintVisitor : public Visitor { void print(uint16_t value, const char* fmt, ...); void print(uint32_t value, const char* fmt, ...); void print(const std::string& value, size_t encoded_size, const char* fmt, ...); - void print_raw(uint32_t length, const char* fmt, ...); std::ostream& stream_; - std::unique_ptr<const ApkAssets> target_apk_; - std::unique_ptr<const ApkAssets> overlay_apk_; + std::vector<std::unique_ptr<const ApkAssets>> apk_assets_; AssetManager2 target_am_; AssetManager2 overlay_am_; size_t offset_; diff --git a/cmds/idmap2/include/idmap2/ResourceMapping.h b/cmds/idmap2/include/idmap2/ResourceMapping.h index 5869409e7db9..0a58ec43d8ff 100644 --- a/cmds/idmap2/include/idmap2/ResourceMapping.h +++ b/cmds/idmap2/include/idmap2/ResourceMapping.h @@ -41,7 +41,7 @@ struct TargetValue { DataValue data_value; }; -using TargetResourceMap = std::map<ResourceId, TargetValue>; +using TargetResourceMap = std::map<ResourceId, std::variant<ResourceId, TargetValue>>; using OverlayResourceMap = std::map<ResourceId, ResourceId>; class ResourceMapping { @@ -56,7 +56,7 @@ class ResourceMapping { bool enforce_overlayable, LogInfo& log_info); // Retrieves the mapping of target resource id to overlay value. - inline TargetResourceMap GetTargetToOverlayMap() const { + inline const TargetResourceMap& GetTargetToOverlayMap() const { return target_map_; } @@ -81,19 +81,24 @@ class ResourceMapping { } // Retrieves the raw string pool data from the xml referenced in android:resourcesMap. - inline const std::pair<const uint8_t*, uint32_t> GetStringPoolData() const { - return std::make_pair(string_pool_data_.get(), string_pool_data_length_); + inline const StringPiece GetStringPoolData() const { + return StringPiece(reinterpret_cast<const char*>(string_pool_data_.get()), + string_pool_data_length_); } private: ResourceMapping() = default; - // Apps a mapping of target resource id to the type and value of the data that overlays the - // target resource. The data_type is the runtime format of the data value (see - // Res_value::dataType). If rewrite_overlay_reference is `true` then references to an overlay + // Maps a target resource id to an overlay resource id. + // If rewrite_overlay_reference is `true` then references to the overlay // resource should appear as a reference to its corresponding target resource at runtime. + Result<Unit> AddMapping(ResourceId target_resource, ResourceId overlay_resource, + bool rewrite_overlay_reference); + + // Maps a target resource id to a data type and value combination. + // The `data_type` is the runtime format of the data value (see Res_value::dataType). Result<Unit> AddMapping(ResourceId target_resource, TargetValue::DataType data_type, - TargetValue::DataValue data_value, bool rewrite_overlay_reference); + TargetValue::DataValue data_value); // Removes the overlay value mapping for the target resource. void RemoveMapping(ResourceId target_resource); diff --git a/cmds/idmap2/libidmap2/BinaryStreamVisitor.cpp b/cmds/idmap2/libidmap2/BinaryStreamVisitor.cpp index 255212ad4c66..726f6c5c2c99 100644 --- a/cmds/idmap2/libidmap2/BinaryStreamVisitor.cpp +++ b/cmds/idmap2/libidmap2/BinaryStreamVisitor.cpp @@ -24,10 +24,6 @@ namespace android::idmap2 { -void BinaryStreamVisitor::Write(const void* value, size_t length) { - stream_.write(reinterpret_cast<const char*>(value), length); -} - void BinaryStreamVisitor::Write8(uint8_t value) { stream_.write(reinterpret_cast<char*>(&value), sizeof(uint8_t)); } @@ -49,11 +45,11 @@ void BinaryStreamVisitor::WriteString256(const StringPiece& value) { stream_.write(buf, sizeof(buf)); } -void BinaryStreamVisitor::WriteString(const std::string& value) { - // pad with null to nearest word boundary; include at least one terminating null - size_t padding_size = 4 - (value.size() % 4); - Write32(value.size() + padding_size); - stream_.write(value.c_str(), value.size()); +void BinaryStreamVisitor::WriteString(const StringPiece& value) { + // pad with null to nearest word boundary; + size_t padding_size = CalculatePadding(value.size()); + Write32(value.size()); + stream_.write(value.data(), value.size()); stream_.write("\0\0\0\0", padding_size); } @@ -67,7 +63,7 @@ void BinaryStreamVisitor::visit(const IdmapHeader& header) { Write32(header.GetTargetCrc()); Write32(header.GetOverlayCrc()); Write32(header.GetFulfilledPolicies()); - Write8(static_cast<uint8_t>(header.GetEnforceOverlayable())); + Write32(static_cast<uint8_t>(header.GetEnforceOverlayable())); WriteString256(header.GetTargetPath()); WriteString256(header.GetOverlayPath()); WriteString(header.GetDebugInfo()); @@ -76,8 +72,16 @@ void BinaryStreamVisitor::visit(const IdmapHeader& header) { void BinaryStreamVisitor::visit(const IdmapData& data) { for (const auto& target_entry : data.GetTargetEntries()) { Write32(target_entry.target_id); - Write8(target_entry.data_type); - Write32(target_entry.data_value); + Write32(target_entry.overlay_id); + } + + static constexpr uint16_t kValueSize = 8U; + for (const auto& target_entry : data.GetTargetInlineEntries()) { + Write32(target_entry.target_id); + Write16(kValueSize); + Write8(0U); // padding + Write8(target_entry.value.data_type); + Write32(target_entry.value.data_value); } for (const auto& overlay_entry : data.GetOverlayEntries()) { @@ -85,16 +89,18 @@ void BinaryStreamVisitor::visit(const IdmapData& data) { Write32(overlay_entry.target_id); } - Write(data.GetStringPoolData(), data.GetHeader()->GetStringPoolLength()); + WriteString(data.GetStringPoolData()); } void BinaryStreamVisitor::visit(const IdmapData::Header& header) { Write8(header.GetTargetPackageId()); Write8(header.GetOverlayPackageId()); + Write8(0U); // padding + Write8(0U); // padding Write32(header.GetTargetEntryCount()); + Write32(header.GetTargetInlineEntryCount()); Write32(header.GetOverlayEntryCount()); Write32(header.GetStringPoolIndexOffset()); - Write32(header.GetStringPoolLength()); } } // namespace android::idmap2 diff --git a/cmds/idmap2/libidmap2/Idmap.cpp b/cmds/idmap2/libidmap2/Idmap.cpp index 23c25a7089de..1129413584b2 100644 --- a/cmds/idmap2/libidmap2/Idmap.cpp +++ b/cmds/idmap2/libidmap2/Idmap.cpp @@ -51,19 +51,19 @@ bool WARN_UNUSED Read8(std::istream& stream, uint8_t* out) { return false; } -bool WARN_UNUSED Read32(std::istream& stream, uint32_t* out) { - uint32_t value; - if (stream.read(reinterpret_cast<char*>(&value), sizeof(uint32_t))) { - *out = dtohl(value); +bool WARN_UNUSED Read16(std::istream& stream, uint16_t* out) { + uint16_t value; + if (stream.read(reinterpret_cast<char*>(&value), sizeof(uint16_t))) { + *out = dtohs(value); return true; } return false; } -bool WARN_UNUSED ReadBuffer(std::istream& stream, std::unique_ptr<uint8_t[]>* out, size_t length) { - auto buffer = std::unique_ptr<uint8_t[]>(new uint8_t[length]); - if (stream.read(reinterpret_cast<char*>(buffer.get()), length)) { - *out = std::move(buffer); +bool WARN_UNUSED Read32(std::istream& stream, uint32_t* out) { + uint32_t value; + if (stream.read(reinterpret_cast<char*>(&value), sizeof(uint32_t))) { + *out = dtohl(value); return true; } return false; @@ -95,8 +95,11 @@ Result<std::string> ReadString(std::istream& stream) { if (!stream.read(buf.data(), size)) { return Error("failed to read string of size %u", size); } - // buf is guaranteed to be null terminated (with enough nulls to end on a word boundary) - buf.resize(strlen(buf.c_str())); + uint32_t padding_size = CalculatePadding(size); + std::string padding(padding_size, '\0'); + if (!stream.read(padding.data(), padding_size)) { + return Error("failed to read string padding of size %u", padding_size); + } return buf; } @@ -112,16 +115,16 @@ Result<uint32_t> GetPackageCrc(const ZipFile& zip) { std::unique_ptr<const IdmapHeader> IdmapHeader::FromBinaryStream(std::istream& stream) { std::unique_ptr<IdmapHeader> idmap_header(new IdmapHeader()); - uint8_t enforce_overlayable; + uint32_t enforce_overlayable; if (!Read32(stream, &idmap_header->magic_) || !Read32(stream, &idmap_header->version_) || !Read32(stream, &idmap_header->target_crc_) || !Read32(stream, &idmap_header->overlay_crc_) || - !Read32(stream, &idmap_header->fulfilled_policies_) || !Read8(stream, &enforce_overlayable) || - !ReadString256(stream, idmap_header->target_path_) || + !Read32(stream, &idmap_header->fulfilled_policies_) || + !Read32(stream, &enforce_overlayable) || !ReadString256(stream, idmap_header->target_path_) || !ReadString256(stream, idmap_header->overlay_path_)) { return nullptr; } - idmap_header->enforce_overlayable_ = static_cast<bool>(enforce_overlayable); + idmap_header->enforce_overlayable_ = enforce_overlayable != 0U; auto debug_str = ReadString(stream); if (!debug_str) { @@ -207,12 +210,13 @@ Result<Unit> IdmapHeader::IsUpToDate(const char* target_path, const char* overla std::unique_ptr<const IdmapData::Header> IdmapData::Header::FromBinaryStream(std::istream& stream) { std::unique_ptr<IdmapData::Header> idmap_data_header(new IdmapData::Header()); + uint8_t padding; if (!Read8(stream, &idmap_data_header->target_package_id_) || - !Read8(stream, &idmap_data_header->overlay_package_id_) || - !Read32(stream, &idmap_data_header->target_entry_count) || + !Read8(stream, &idmap_data_header->overlay_package_id_) || !Read8(stream, &padding) || + !Read8(stream, &padding) || !Read32(stream, &idmap_data_header->target_entry_count) || + !Read32(stream, &idmap_data_header->target_entry_inline_count) || !Read32(stream, &idmap_data_header->overlay_entry_count) || - !Read32(stream, &idmap_data_header->string_pool_index_offset) || - !Read32(stream, &idmap_data_header->string_pool_len)) { + !Read32(stream, &idmap_data_header->string_pool_index_offset)) { return nullptr; } @@ -225,14 +229,27 @@ std::unique_ptr<const IdmapData> IdmapData::FromBinaryStream(std::istream& strea if (!data->header_) { return nullptr; } + // Read the mapping of target resource id to overlay resource value. for (size_t i = 0; i < data->header_->GetTargetEntryCount(); i++) { TargetEntry target_entry{}; - if (!Read32(stream, &target_entry.target_id) || !Read8(stream, &target_entry.data_type) || - !Read32(stream, &target_entry.data_value)) { + if (!Read32(stream, &target_entry.target_id) || !Read32(stream, &target_entry.overlay_id)) { + return nullptr; + } + data->target_entries_.push_back(target_entry); + } + + // Read the mapping of target resource id to inline overlay values. + uint8_t unused1; + uint16_t unused2; + for (size_t i = 0; i < data->header_->GetTargetInlineEntryCount(); i++) { + TargetInlineEntry target_entry{}; + if (!Read32(stream, &target_entry.target_id) || !Read16(stream, &unused2) || + !Read8(stream, &unused1) || !Read8(stream, &target_entry.value.data_type) || + !Read32(stream, &target_entry.value.data_value)) { return nullptr; } - data->target_entries_.emplace_back(target_entry); + data->target_inline_entries_.push_back(target_entry); } // Read the mapping of overlay resource id to target resource id. @@ -245,9 +262,11 @@ std::unique_ptr<const IdmapData> IdmapData::FromBinaryStream(std::istream& strea } // Read raw string pool bytes. - if (!ReadBuffer(stream, &data->string_pool_, data->header_->string_pool_len)) { + auto string_pool_data = ReadString(stream); + if (!string_pool_data) { return nullptr; } + data->string_pool_data_ = std::move(*string_pool_data); return std::move(data); } @@ -290,27 +309,28 @@ Result<std::unique_ptr<const IdmapData>> IdmapData::FromResourceMapping( } std::unique_ptr<IdmapData> data(new IdmapData()); - for (const auto& mappings : resource_mapping.GetTargetToOverlayMap()) { - data->target_entries_.emplace_back(IdmapData::TargetEntry{ - mappings.first, mappings.second.data_type, mappings.second.data_value}); + data->string_pool_data_ = resource_mapping.GetStringPoolData().to_string(); + for (const auto& mapping : resource_mapping.GetTargetToOverlayMap()) { + if (auto overlay_resource = std::get_if<ResourceId>(&mapping.second)) { + data->target_entries_.push_back({mapping.first, *overlay_resource}); + } else { + data->target_inline_entries_.push_back( + {mapping.first, std::get<TargetValue>(mapping.second)}); + } } - for (const auto& mappings : resource_mapping.GetOverlayToTargetMap()) { - data->overlay_entries_.emplace_back(IdmapData::OverlayEntry{mappings.first, mappings.second}); + for (const auto& mapping : resource_mapping.GetOverlayToTargetMap()) { + data->overlay_entries_.emplace_back(IdmapData::OverlayEntry{mapping.first, mapping.second}); } std::unique_ptr<IdmapData::Header> data_header(new IdmapData::Header()); data_header->target_package_id_ = resource_mapping.GetTargetPackageId(); data_header->overlay_package_id_ = resource_mapping.GetOverlayPackageId(); data_header->target_entry_count = static_cast<uint32_t>(data->target_entries_.size()); + data_header->target_entry_inline_count = + static_cast<uint32_t>(data->target_inline_entries_.size()); data_header->overlay_entry_count = static_cast<uint32_t>(data->overlay_entries_.size()); data_header->string_pool_index_offset = resource_mapping.GetStringPoolOffset(); - - const auto string_pool_data = resource_mapping.GetStringPoolData(); - data_header->string_pool_len = string_pool_data.second; - data->string_pool_ = std::unique_ptr<uint8_t[]>(new uint8_t[data_header->string_pool_len]); - memcpy(data->string_pool_.get(), string_pool_data.first, data_header->string_pool_len); - data->header_ = std::move(data_header); return {std::move(data)}; } diff --git a/cmds/idmap2/libidmap2/PrettyPrintVisitor.cpp b/cmds/idmap2/libidmap2/PrettyPrintVisitor.cpp index 63ee8a648352..a93202a64d31 100644 --- a/cmds/idmap2/libidmap2/PrettyPrintVisitor.cpp +++ b/cmds/idmap2/libidmap2/PrettyPrintVisitor.cpp @@ -38,6 +38,7 @@ void PrettyPrintVisitor::visit(const IdmapHeader& header) { stream_ << "Paths:" << std::endl << TAB "target apk path : " << header.GetTargetPath() << std::endl << TAB "overlay apk path : " << header.GetOverlayPath() << std::endl; + const std::string& debug = header.GetDebugInfo(); if (!debug.empty()) { std::istringstream debug_stream(debug); @@ -48,10 +49,16 @@ void PrettyPrintVisitor::visit(const IdmapHeader& header) { } } - target_apk_ = ApkAssets::Load(header.GetTargetPath().to_string()); - if (target_apk_) { + if (auto target_apk_ = ApkAssets::Load(header.GetTargetPath().to_string())) { target_am_.SetApkAssets({target_apk_.get()}); + apk_assets_.push_back(std::move(target_apk_)); + } + + if (auto overlay_apk = ApkAssets::Load(header.GetOverlayPath().to_string())) { + overlay_am_.SetApkAssets({overlay_apk.get()}); + apk_assets_.push_back(std::move(overlay_apk)); } + stream_ << "Mapping:" << std::endl; } @@ -59,34 +66,56 @@ void PrettyPrintVisitor::visit(const IdmapData::Header& header ATTRIBUTE_UNUSED) } void PrettyPrintVisitor::visit(const IdmapData& data) { + static constexpr const char* kUnknownResourceName = "???"; + const bool target_package_loaded = !target_am_.GetApkAssets().empty(); - const ResStringPool string_pool(data.GetStringPoolData(), - data.GetHeader()->GetStringPoolLength()); + const bool overlay_package_loaded = !overlay_am_.GetApkAssets().empty(); + + const ResStringPool string_pool(data.GetStringPoolData().data(), data.GetStringPoolData().size()); const size_t string_pool_offset = data.GetHeader()->GetStringPoolIndexOffset(); - for (auto& target_entry : data.GetTargetEntries()) { - stream_ << TAB << base::StringPrintf("0x%08x ->", target_entry.target_id); + for (const auto& target_entry : data.GetTargetEntries()) { + std::string target_name = kUnknownResourceName; + if (target_package_loaded) { + if (auto name = utils::ResToTypeEntryName(target_am_, target_entry.target_id)) { + target_name = *name; + } + } - if (target_entry.data_type != Res_value::TYPE_REFERENCE && - target_entry.data_type != Res_value::TYPE_DYNAMIC_REFERENCE) { - stream_ << " " << utils::DataTypeToString(target_entry.data_type); + std::string overlay_name = kUnknownResourceName; + if (overlay_package_loaded) { + if (auto name = utils::ResToTypeEntryName(overlay_am_, target_entry.overlay_id)) { + overlay_name = *name; + } } - if (target_entry.data_type == Res_value::TYPE_STRING) { - stream_ << " \"" - << string_pool.string8ObjectAt(target_entry.data_value - string_pool_offset).c_str() - << "\""; + stream_ << TAB + << base::StringPrintf("0x%08x -> 0x%08x (%s -> %s)", target_entry.target_id, + target_entry.overlay_id, target_name.c_str(), + overlay_name.c_str()) + << std::endl; + } + + for (auto& target_entry : data.GetTargetInlineEntries()) { + stream_ << TAB << base::StringPrintf("0x%08x -> ", target_entry.target_id) + << utils::DataTypeToString(target_entry.value.data_type); + + size_t unused; + if (target_entry.value.data_type == Res_value::TYPE_STRING) { + auto str = string_pool.stringAt(target_entry.value.data_value - string_pool_offset, &unused); + stream_ << " \"" << StringPiece16(str) << "\""; } else { - stream_ << " " << base::StringPrintf("0x%08x", target_entry.data_value); + stream_ << " " << base::StringPrintf("0x%08x", target_entry.value.data_value); } + std::string target_name = kUnknownResourceName; if (target_package_loaded) { - Result<std::string> name = utils::ResToTypeEntryName(target_am_, target_entry.target_id); - if (name) { - stream_ << " " << *name; + if (auto name = utils::ResToTypeEntryName(target_am_, target_entry.target_id)) { + target_name = *name; } } - stream_ << std::endl; + + stream_ << " (" << target_name << ")" << std::endl; } } diff --git a/cmds/idmap2/libidmap2/RawPrintVisitor.cpp b/cmds/idmap2/libidmap2/RawPrintVisitor.cpp index 3f62a2ae2029..82f5d26cbbb3 100644 --- a/cmds/idmap2/libidmap2/RawPrintVisitor.cpp +++ b/cmds/idmap2/libidmap2/RawPrintVisitor.cpp @@ -30,15 +30,6 @@ using android::ApkAssets; using android::idmap2::policy::PoliciesToDebugString; -namespace { - -size_t StringSizeWhenEncoded(const std::string& s) { - size_t null_bytes = 4 - (s.size() % 4); - return sizeof(uint32_t) + s.size() + null_bytes; -} - -} // namespace - namespace android::idmap2 { void RawPrintVisitor::visit(const Idmap& idmap ATTRIBUTE_UNUSED) { @@ -51,19 +42,24 @@ void RawPrintVisitor::visit(const IdmapHeader& header) { print(header.GetOverlayCrc(), "overlay crc"); print(header.GetFulfilledPolicies(), "fulfilled policies: %s", PoliciesToDebugString(header.GetFulfilledPolicies()).c_str()); - print(static_cast<uint8_t>(header.GetEnforceOverlayable()), "enforce overlayable"); + print(static_cast<uint32_t>(header.GetEnforceOverlayable()), "enforce overlayable"); print(header.GetTargetPath().to_string(), kIdmapStringLength, "target path"); print(header.GetOverlayPath().to_string(), kIdmapStringLength, "overlay path"); - print("...", StringSizeWhenEncoded(header.GetDebugInfo()), "debug info"); - target_apk_ = ApkAssets::Load(header.GetTargetPath().to_string()); + uint32_t debug_info_size = header.GetDebugInfo().size(); + print(debug_info_size, "debug info size"); + print("...", debug_info_size + CalculatePadding(debug_info_size), "debug info"); + + auto target_apk_ = ApkAssets::Load(header.GetTargetPath().to_string()); if (target_apk_) { target_am_.SetApkAssets({target_apk_.get()}); + apk_assets_.push_back(std::move(target_apk_)); } - overlay_apk_ = ApkAssets::Load(header.GetOverlayPath().to_string()); + auto overlay_apk_ = ApkAssets::Load(header.GetOverlayPath().to_string()); if (overlay_apk_) { overlay_am_.SetApkAssets({overlay_apk_.get()}); + apk_assets_.push_back(std::move(overlay_apk_)); } } @@ -82,18 +78,44 @@ void RawPrintVisitor::visit(const IdmapData& data ATTRIBUTE_UNUSED) { print(target_entry.target_id, "target id"); } - print(target_entry.data_type, "type: %s", - utils::DataTypeToString(target_entry.data_type).data()); + Result<std::string> overlay_name(Error("")); + if (overlay_package_loaded) { + overlay_name = utils::ResToTypeEntryName(overlay_am_, target_entry.overlay_id); + } + if (overlay_name) { + print(target_entry.overlay_id, "overlay id: %s", overlay_name->c_str()); + } else { + print(target_entry.overlay_id, "overlay id"); + } + } + + for (auto& target_entry : data.GetTargetInlineEntries()) { + Result<std::string> target_name(Error("")); + if (target_package_loaded) { + target_name = utils::ResToTypeEntryName(target_am_, target_entry.target_id); + } + if (target_name) { + print(target_entry.target_id, "target id: %s", target_name->c_str()); + } else { + print(target_entry.target_id, "target id"); + } + + print("...", sizeof(Res_value::size) + sizeof(Res_value::res0), "padding"); + + print(target_entry.value.data_type, "type: %s", + utils::DataTypeToString(target_entry.value.data_type).data()); Result<std::string> overlay_name(Error("")); - if (overlay_package_loaded && (target_entry.data_type == Res_value::TYPE_REFERENCE || - target_entry.data_type == Res_value::TYPE_DYNAMIC_REFERENCE)) { - overlay_name = utils::ResToTypeEntryName(overlay_am_, target_entry.data_value); + if (overlay_package_loaded && + (target_entry.value.data_value == Res_value::TYPE_REFERENCE || + target_entry.value.data_value == Res_value::TYPE_DYNAMIC_REFERENCE)) { + overlay_name = utils::ResToTypeEntryName(overlay_am_, target_entry.value.data_value); } + if (overlay_name) { - print(target_entry.data_value, "value: %s", overlay_name->c_str()); + print(target_entry.value.data_value, "data: %s", overlay_name->c_str()); } else { - print(target_entry.data_value, "value"); + print(target_entry.value.data_value, "data"); } } @@ -121,19 +143,19 @@ void RawPrintVisitor::visit(const IdmapData& data ATTRIBUTE_UNUSED) { } } - const size_t string_pool_length = data.GetHeader()->GetStringPoolLength(); - if (string_pool_length > 0) { - print_raw(string_pool_length, "%zu raw string pool bytes", string_pool_length); - } + uint32_t string_pool_size = data.GetStringPoolData().size(); + print(string_pool_size, "string pool size"); + print("...", string_pool_size + CalculatePadding(string_pool_size), "string pool"); } void RawPrintVisitor::visit(const IdmapData::Header& header) { print(header.GetTargetPackageId(), "target package id"); print(header.GetOverlayPackageId(), "overlay package id"); + print("...", sizeof(Idmap_data_header::p0), "padding"); print(header.GetTargetEntryCount(), "target entry count"); + print(header.GetTargetInlineEntryCount(), "target inline entry count"); print(header.GetOverlayEntryCount(), "overlay entry count"); print(header.GetStringPoolIndexOffset(), "string pool index offset"); - print(header.GetStringPoolLength(), "string pool byte length"); } // NOLINTNEXTLINE(cert-dcl50-cpp) @@ -190,17 +212,4 @@ void RawPrintVisitor::print(const std::string& value, size_t encoded_size, const offset_ += encoded_size; } -// NOLINTNEXTLINE(cert-dcl50-cpp) -void RawPrintVisitor::print_raw(uint32_t length, const char* fmt, ...) { - va_list ap; - va_start(ap, fmt); - std::string comment; - base::StringAppendV(&comment, fmt, ap); - va_end(ap); - - stream_ << base::StringPrintf("%08zx: ", offset_) << "........ " << comment << std::endl; - - offset_ += length; -} - } // namespace android::idmap2 diff --git a/cmds/idmap2/libidmap2/ResourceMapping.cpp b/cmds/idmap2/libidmap2/ResourceMapping.cpp index fd8b4eb86b4a..31f1c16ba5a6 100644 --- a/cmds/idmap2/libidmap2/ResourceMapping.cpp +++ b/cmds/idmap2/libidmap2/ResourceMapping.cpp @@ -71,9 +71,9 @@ Result<Unit> CheckOverlayable(const LoadedPackage& target_package, if (!target_package.DefinesOverlayable()) { return (sDefaultPolicies & fulfilled_policies) != 0 ? Result<Unit>({}) - : Error( - "overlay must be preinstalled or signed with the same signature as the " - "target"); + : Error("overlay must be preinstalled, signed with the same signature as the target," + " or signed with the same signature as the package referenced through" + " <overlay-config-signature>."); } const OverlayableInfo* overlayable_info = target_package.GetOverlayableInfo(target_resource); @@ -205,19 +205,14 @@ Result<ResourceMapping> ResourceMapping::CreateResourceMapping(const AssetManage overlay_resource->data += string_pool_offset; } - // Only rewrite resources defined within the overlay package to their corresponding target - // resource ids at runtime. - bool rewrite_overlay_reference = - IsReference(overlay_resource->dataType) - ? overlay_package_id == EXTRACT_PACKAGE(overlay_resource->data) - : false; - - if (rewrite_overlay_reference) { - overlay_resource->dataType = Res_value::TYPE_DYNAMIC_REFERENCE; + if (IsReference(overlay_resource->dataType)) { + // Only rewrite resources defined within the overlay package to their corresponding target + // resource ids at runtime. + bool rewrite_reference = overlay_package_id == EXTRACT_PACKAGE(overlay_resource->data); + resource_mapping.AddMapping(target_id, overlay_resource->data, rewrite_reference); + } else { + resource_mapping.AddMapping(target_id, overlay_resource->dataType, overlay_resource->data); } - - resource_mapping.AddMapping(target_id, overlay_resource->dataType, overlay_resource->data, - rewrite_overlay_reference); } return resource_mapping; @@ -246,9 +241,8 @@ Result<ResourceMapping> ResourceMapping::CreateResourceMappingLegacy( // Retrieve the compile-time resource id of the target resource. target_resource = REWRITE_PACKAGE(target_resource, target_package_id); - - resource_mapping.AddMapping(target_resource, Res_value::TYPE_REFERENCE, overlay_resid, - /* rewrite_overlay_reference */ false); + resource_mapping.AddMapping(target_resource, overlay_resid, + false /* rewrite_overlay_reference */); } return resource_mapping; @@ -396,9 +390,7 @@ OverlayResourceMap ResourceMapping::GetOverlayToTargetMap() const { return map; } -Result<Unit> ResourceMapping::AddMapping(ResourceId target_resource, - TargetValue::DataType data_type, - TargetValue::DataValue data_value, +Result<Unit> ResourceMapping::AddMapping(ResourceId target_resource, ResourceId overlay_resource, bool rewrite_overlay_reference) { if (target_map_.find(target_resource) != target_map_.end()) { return Error(R"(target resource id "0x%08x" mapped to multiple values)", target_resource); @@ -407,13 +399,26 @@ Result<Unit> ResourceMapping::AddMapping(ResourceId target_resource, // TODO(141485591): Ensure that the overlay type is compatible with the target type. If the // runtime types are not compatible, it could cause runtime crashes when the resource is resolved. - target_map_.insert(std::make_pair(target_resource, TargetValue{data_type, data_value})); + target_map_.insert(std::make_pair(target_resource, overlay_resource)); - if (rewrite_overlay_reference && IsReference(data_type)) { - overlay_map_.insert(std::make_pair(data_value, target_resource)); + if (rewrite_overlay_reference) { + overlay_map_.insert(std::make_pair(overlay_resource, target_resource)); } + return Unit{}; +} - return Result<Unit>({}); +Result<Unit> ResourceMapping::AddMapping(ResourceId target_resource, + TargetValue::DataType data_type, + TargetValue::DataValue data_value) { + if (target_map_.find(target_resource) != target_map_.end()) { + return Error(R"(target resource id "0x%08x" mapped to multiple values)", target_resource); + } + + // TODO(141485591): Ensure that the overlay type is compatible with the target type. If the + // runtime types are not compatible, it could cause runtime crashes when the resource is resolved. + + target_map_.insert(std::make_pair(target_resource, TargetValue{data_type, data_value})); + return Unit{}; } void ResourceMapping::RemoveMapping(ResourceId target_resource) { @@ -422,14 +427,15 @@ void ResourceMapping::RemoveMapping(ResourceId target_resource) { return; } - const TargetValue value = target_iter->second; + const auto value = target_iter->second; target_map_.erase(target_iter); - if (!IsReference(value.data_type)) { + const ResourceId* overlay_resource = std::get_if<ResourceId>(&value); + if (overlay_resource == nullptr) { return; } - auto overlay_iter = overlay_map_.equal_range(value.data_value); + auto overlay_iter = overlay_map_.equal_range(*overlay_resource); for (auto i = overlay_iter.first; i != overlay_iter.second; ++i) { if (i->second == target_resource) { overlay_map_.erase(i); diff --git a/cmds/idmap2/tests/BinaryStreamVisitorTests.cpp b/cmds/idmap2/tests/BinaryStreamVisitorTests.cpp index 5fea7bcdaac5..c3a3e0ba9047 100644 --- a/cmds/idmap2/tests/BinaryStreamVisitorTests.cpp +++ b/cmds/idmap2/tests/BinaryStreamVisitorTests.cpp @@ -72,13 +72,20 @@ TEST(BinaryStreamVisitorTests, CreateBinaryStreamViaBinaryStreamVisitor) { const auto& target_entries2 = data2->GetTargetEntries(); ASSERT_EQ(target_entries1.size(), target_entries2.size()); ASSERT_EQ(target_entries1[0].target_id, target_entries2[0].target_id); - ASSERT_EQ(target_entries1[0].data_value, target_entries2[0].data_value); + ASSERT_EQ(target_entries1[0].overlay_id, target_entries2[0].overlay_id); ASSERT_EQ(target_entries1[1].target_id, target_entries2[1].target_id); - ASSERT_EQ(target_entries1[1].data_value, target_entries2[1].data_value); + ASSERT_EQ(target_entries1[1].overlay_id, target_entries2[1].overlay_id); ASSERT_EQ(target_entries1[2].target_id, target_entries2[2].target_id); - ASSERT_EQ(target_entries1[2].data_value, target_entries2[2].data_value); + ASSERT_EQ(target_entries1[2].overlay_id, target_entries2[2].overlay_id); + + const auto& target_inline_entries1 = data1->GetTargetInlineEntries(); + const auto& target_inline_entries2 = data2->GetTargetInlineEntries(); + ASSERT_EQ(target_inline_entries1.size(), target_inline_entries2.size()); + ASSERT_EQ(target_inline_entries1[0].target_id, target_inline_entries2[0].target_id); + ASSERT_EQ(target_inline_entries1[0].value.data_type, target_inline_entries2[0].value.data_type); + ASSERT_EQ(target_inline_entries1[0].value.data_value, target_inline_entries2[0].value.data_value); const auto& overlay_entries1 = data1->GetOverlayEntries(); const auto& overlay_entries2 = data2->GetOverlayEntries(); diff --git a/cmds/idmap2/tests/Idmap2BinaryTests.cpp b/cmds/idmap2/tests/Idmap2BinaryTests.cpp index 61751b33dcba..e7e9e4cf5091 100644 --- a/cmds/idmap2/tests/Idmap2BinaryTests.cpp +++ b/cmds/idmap2/tests/Idmap2BinaryTests.cpp @@ -128,13 +128,13 @@ TEST_F(Idmap2BinaryTests, Dump) { // clang-format on ASSERT_THAT(result, NotNull()); ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr; - ASSERT_NE(result->stdout.find(R::target::integer::literal::int1 + " -> 0x7f010000 integer/int1"), + ASSERT_NE(result->stdout.find(R::target::integer::literal::int1 + " -> 0x7f010000"), std::string::npos); - ASSERT_NE(result->stdout.find(R::target::string::literal::str1 + " -> 0x7f020000 string/str1"), + ASSERT_NE(result->stdout.find(R::target::string::literal::str1 + " -> 0x7f020000"), std::string::npos); - ASSERT_NE(result->stdout.find(R::target::string::literal::str3 + " -> 0x7f020001 string/str3"), + ASSERT_NE(result->stdout.find(R::target::string::literal::str3 + " -> 0x7f020001"), std::string::npos); - ASSERT_NE(result->stdout.find(R::target::string::literal::str4 + " -> 0x7f020002 string/str4"), + ASSERT_NE(result->stdout.find(R::target::string::literal::str4 + " -> 0x7f020002"), std::string::npos); // clang-format off diff --git a/cmds/idmap2/tests/IdmapTests.cpp b/cmds/idmap2/tests/IdmapTests.cpp index 6fab5e0f8ae1..9b42a2781b58 100644 --- a/cmds/idmap2/tests/IdmapTests.cpp +++ b/cmds/idmap2/tests/IdmapTests.cpp @@ -42,14 +42,18 @@ using PolicyFlags = android::ResTable_overlayable_policy_header::PolicyFlags; namespace android::idmap2 { -#define ASSERT_TARGET_ENTRY(entry, target_resid, type, value) \ - ASSERT_EQ(entry.target_id, target_resid); \ - ASSERT_EQ(entry.data_type, type); \ - ASSERT_EQ(entry.data_value, value) +#define ASSERT_TARGET_ENTRY(entry, target_resid, overlay_resid) \ + ASSERT_EQ((entry).target_id, (target_resid)); \ + ASSERT_EQ((entry).overlay_id, (overlay_resid)) + +#define ASSERT_TARGET_INLINE_ENTRY(entry, target_resid, expected_type, expected_value) \ + ASSERT_EQ((entry).target_id, target_resid); \ + ASSERT_EQ((entry).value.data_type, (expected_type)); \ + ASSERT_EQ((entry).value.data_value, (expected_value)) #define ASSERT_OVERLAY_ENTRY(entry, overlay_resid, target_resid) \ - ASSERT_EQ(entry.overlay_id, overlay_resid); \ - ASSERT_EQ(entry.target_id, target_resid) + ASSERT_EQ((entry).overlay_id, (overlay_resid)); \ + ASSERT_EQ((entry).target_id, (target_resid)) TEST(IdmapTests, TestCanonicalIdmapPathFor) { ASSERT_EQ(Idmap::CanonicalIdmapPathFor("/foo", "/vendor/overlay/bar.apk"), @@ -62,7 +66,7 @@ TEST(IdmapTests, CreateIdmapHeaderFromBinaryStream) { std::unique_ptr<const IdmapHeader> header = IdmapHeader::FromBinaryStream(stream); ASSERT_THAT(header, NotNull()); ASSERT_EQ(header->GetMagic(), 0x504d4449U); - ASSERT_EQ(header->GetVersion(), 0x04U); + ASSERT_EQ(header->GetVersion(), 0x05U); ASSERT_EQ(header->GetTargetCrc(), 0x1234U); ASSERT_EQ(header->GetOverlayCrc(), 0x5678U); ASSERT_EQ(header->GetFulfilledPolicies(), 0x11); @@ -75,7 +79,7 @@ TEST(IdmapTests, CreateIdmapHeaderFromBinaryStream) { TEST(IdmapTests, FailToCreateIdmapHeaderFromBinaryStreamIfPathTooLong) { std::string raw(reinterpret_cast<const char*>(idmap_raw_data), idmap_raw_data_len); // overwrite the target path string, including the terminating null, with '.' - for (size_t i = 0x15; i < 0x115; i++) { + for (size_t i = 0x18; i < 0x118; i++) { raw[i] = '.'; } std::istringstream stream(raw); @@ -84,7 +88,7 @@ TEST(IdmapTests, FailToCreateIdmapHeaderFromBinaryStreamIfPathTooLong) { } TEST(IdmapTests, CreateIdmapDataHeaderFromBinaryStream) { - const size_t offset = 0x221; + const size_t offset = 0x224; std::string raw(reinterpret_cast<const char*>(idmap_raw_data + offset), idmap_raw_data_len - offset); std::istringstream stream(raw); @@ -96,7 +100,7 @@ TEST(IdmapTests, CreateIdmapDataHeaderFromBinaryStream) { } TEST(IdmapTests, CreateIdmapDataFromBinaryStream) { - const size_t offset = 0x221; + const size_t offset = 0x224; std::string raw(reinterpret_cast<const char*>(idmap_raw_data + offset), idmap_raw_data_len - offset); std::istringstream stream(raw); @@ -106,12 +110,14 @@ TEST(IdmapTests, CreateIdmapDataFromBinaryStream) { const auto& target_entries = data->GetTargetEntries(); ASSERT_EQ(target_entries.size(), 3U); - ASSERT_TARGET_ENTRY(target_entries[0], 0x7f020000, 0x01 /* Res_value::TYPE_REFERENCE */, - 0x7f020000); - ASSERT_TARGET_ENTRY(target_entries[1], 0x7f030000, 0x01 /* Res_value::TYPE_REFERENCE */, - 0x7f030000); - ASSERT_TARGET_ENTRY(target_entries[2], 0x7f030002, 0x01 /* Res_value::TYPE_REFERENCE */, - 0x7f030001); + ASSERT_TARGET_ENTRY(target_entries[0], 0x7f020000, 0x7f020000); + ASSERT_TARGET_ENTRY(target_entries[1], 0x7f030000, 0x7f030000); + ASSERT_TARGET_ENTRY(target_entries[2], 0x7f030002, 0x7f030001); + + const auto& target_inline_entries = data->GetTargetInlineEntries(); + ASSERT_EQ(target_inline_entries.size(), 1U); + ASSERT_TARGET_INLINE_ENTRY(target_inline_entries[0], 0x7f040000, Res_value::TYPE_INT_HEX, + 0x12345678); const auto& overlay_entries = data->GetOverlayEntries(); ASSERT_EQ(target_entries.size(), 3U); @@ -130,7 +136,7 @@ TEST(IdmapTests, CreateIdmapFromBinaryStream) { ASSERT_THAT(idmap->GetHeader(), NotNull()); ASSERT_EQ(idmap->GetHeader()->GetMagic(), 0x504d4449U); - ASSERT_EQ(idmap->GetHeader()->GetVersion(), 0x04U); + ASSERT_EQ(idmap->GetHeader()->GetVersion(), 0x05U); ASSERT_EQ(idmap->GetHeader()->GetTargetCrc(), 0x1234U); ASSERT_EQ(idmap->GetHeader()->GetOverlayCrc(), 0x5678U); ASSERT_EQ(idmap->GetHeader()->GetFulfilledPolicies(), 0x11); @@ -146,9 +152,14 @@ TEST(IdmapTests, CreateIdmapFromBinaryStream) { const auto& target_entries = data->GetTargetEntries(); ASSERT_EQ(target_entries.size(), 3U); - ASSERT_TARGET_ENTRY(target_entries[0], 0x7f020000, Res_value::TYPE_REFERENCE, 0x7f020000); - ASSERT_TARGET_ENTRY(target_entries[1], 0x7f030000, Res_value::TYPE_REFERENCE, 0x7f030000); - ASSERT_TARGET_ENTRY(target_entries[2], 0x7f030002, Res_value::TYPE_REFERENCE, 0x7f030001); + ASSERT_TARGET_ENTRY(target_entries[0], 0x7f020000, 0x7f020000); + ASSERT_TARGET_ENTRY(target_entries[1], 0x7f030000, 0x7f030000); + ASSERT_TARGET_ENTRY(target_entries[2], 0x7f030002, 0x7f030001); + + const auto& target_inline_entries = data->GetTargetInlineEntries(); + ASSERT_EQ(target_inline_entries.size(), 1U); + ASSERT_TARGET_INLINE_ENTRY(target_inline_entries[0], 0x7f040000, Res_value::TYPE_INT_HEX, + 0x12345678); const auto& overlay_entries = data->GetOverlayEntries(); ASSERT_EQ(target_entries.size(), 3U); @@ -184,7 +195,7 @@ TEST(IdmapTests, CreateIdmapHeaderFromApkAssets) { ASSERT_THAT(idmap->GetHeader(), NotNull()); ASSERT_EQ(idmap->GetHeader()->GetMagic(), 0x504d4449U); - ASSERT_EQ(idmap->GetHeader()->GetVersion(), 0x04U); + ASSERT_EQ(idmap->GetHeader()->GetVersion(), 0x05U); ASSERT_EQ(idmap->GetHeader()->GetTargetCrc(), android::idmap2::TestConstants::TARGET_CRC); ASSERT_EQ(idmap->GetHeader()->GetOverlayCrc(), android::idmap2::TestConstants::OVERLAY_CRC); ASSERT_EQ(idmap->GetHeader()->GetFulfilledPolicies(), PolicyFlags::PUBLIC); @@ -244,14 +255,13 @@ TEST(IdmapTests, CreateIdmapDataFromApkAssets) { const auto& target_entries = data->GetTargetEntries(); ASSERT_EQ(target_entries.size(), 4U); - ASSERT_TARGET_ENTRY(target_entries[0], R::target::integer::int1, - Res_value::TYPE_DYNAMIC_REFERENCE, R::overlay::integer::int1); - ASSERT_TARGET_ENTRY(target_entries[1], R::target::string::str1, Res_value::TYPE_DYNAMIC_REFERENCE, - R::overlay::string::str1); - ASSERT_TARGET_ENTRY(target_entries[2], R::target::string::str3, Res_value::TYPE_DYNAMIC_REFERENCE, - R::overlay::string::str3); - ASSERT_TARGET_ENTRY(target_entries[3], R::target::string::str4, Res_value::TYPE_DYNAMIC_REFERENCE, - R::overlay::string::str4); + ASSERT_TARGET_ENTRY(target_entries[0], R::target::integer::int1, R::overlay::integer::int1); + ASSERT_TARGET_ENTRY(target_entries[1], R::target::string::str1, R::overlay::string::str1); + ASSERT_TARGET_ENTRY(target_entries[2], R::target::string::str3, R::overlay::string::str3); + ASSERT_TARGET_ENTRY(target_entries[3], R::target::string::str4, R::overlay::string::str4); + + const auto& target_inline_entries = data->GetTargetInlineEntries(); + ASSERT_EQ(target_inline_entries.size(), 0U); const auto& overlay_entries = data->GetOverlayEntries(); ASSERT_EQ(target_entries.size(), 4U); @@ -286,13 +296,13 @@ TEST(IdmapTests, CreateIdmapDataFromApkAssetsSharedLibOverlay) { const auto& target_entries = data->GetTargetEntries(); ASSERT_EQ(target_entries.size(), 4U); ASSERT_TARGET_ENTRY(target_entries[0], R::target::integer::int1, - Res_value::TYPE_DYNAMIC_REFERENCE, R::overlay_shared::integer::int1); - ASSERT_TARGET_ENTRY(target_entries[1], R::target::string::str1, Res_value::TYPE_DYNAMIC_REFERENCE, - R::overlay_shared::string::str1); - ASSERT_TARGET_ENTRY(target_entries[2], R::target::string::str3, Res_value::TYPE_DYNAMIC_REFERENCE, - R::overlay_shared::string::str3); - ASSERT_TARGET_ENTRY(target_entries[3], R::target::string::str4, Res_value::TYPE_DYNAMIC_REFERENCE, - R::overlay_shared::string::str4); + R::overlay_shared::integer::int1); + ASSERT_TARGET_ENTRY(target_entries[1], R::target::string::str1, R::overlay_shared::string::str1); + ASSERT_TARGET_ENTRY(target_entries[2], R::target::string::str3, R::overlay_shared::string::str3); + ASSERT_TARGET_ENTRY(target_entries[3], R::target::string::str4, R::overlay_shared::string::str4); + + const auto& target_inline_entries = data->GetTargetInlineEntries(); + ASSERT_EQ(target_inline_entries.size(), 0U); const auto& overlay_entries = data->GetOverlayEntries(); ASSERT_EQ(target_entries.size(), 4U); @@ -320,10 +330,12 @@ TEST(IdmapTests, CreateIdmapDataDoNotRewriteNonOverlayResourceId) { const auto& target_entries = data->GetTargetEntries(); ASSERT_EQ(target_entries.size(), 2U); - ASSERT_TARGET_ENTRY(target_entries[0], R::target::string::str1, Res_value::TYPE_REFERENCE, + ASSERT_TARGET_ENTRY(target_entries[0], R::target::string::str1, 0x0104000a); // -> android:string/ok - ASSERT_TARGET_ENTRY(target_entries[1], R::target::string::str3, Res_value::TYPE_DYNAMIC_REFERENCE, - R::overlay::string::str3); + ASSERT_TARGET_ENTRY(target_entries[1], R::target::string::str3, R::overlay::string::str3); + + const auto& target_inline_entries = data->GetTargetInlineEntries(); + ASSERT_EQ(target_inline_entries.size(), 0U); const auto& overlay_entries = data->GetOverlayEntries(); ASSERT_EQ(overlay_entries.size(), 1U); @@ -342,13 +354,17 @@ TEST(IdmapTests, CreateIdmapDataInlineResources) { ASSERT_TRUE(idmap_data) << idmap_data.GetErrorMessage(); auto& data = *idmap_data; - constexpr size_t overlay_string_pool_size = 8U; const auto& target_entries = data->GetTargetEntries(); - ASSERT_EQ(target_entries.size(), 2U); - ASSERT_TARGET_ENTRY(target_entries[0], R::target::integer::int1, Res_value::TYPE_INT_DEC, - 73U); // -> 73 - ASSERT_TARGET_ENTRY(target_entries[1], R::target::string::str1, Res_value::TYPE_STRING, - overlay_string_pool_size + 0U); // -> "Hello World" + ASSERT_EQ(target_entries.size(), 0U); + + constexpr size_t overlay_string_pool_size = 8U; + const auto& target_inline_entries = data->GetTargetInlineEntries(); + ASSERT_EQ(target_inline_entries.size(), 2U); + ASSERT_TARGET_INLINE_ENTRY(target_inline_entries[0], R::target::integer::int1, + Res_value::TYPE_INT_DEC, 73U); // -> 73 + ASSERT_TARGET_INLINE_ENTRY(target_inline_entries[1], R::target::string::str1, + Res_value::TYPE_STRING, + overlay_string_pool_size + 0U); // -> "Hello World" const auto& overlay_entries = data->GetOverlayEntries(); ASSERT_EQ(overlay_entries.size(), 0U); @@ -479,9 +495,9 @@ TEST(IdmapTests, IdmapHeaderIsUpToDate) { ASSERT_FALSE(bad_enforce_header->IsUpToDate(target_apk_path.c_str(), overlay_apk_path.c_str(), PolicyFlags::PUBLIC, /* enforce_overlayable */ true)); - // target path: bytes (0x15, 0x114) + // target path: bytes (0x18, 0x117) std::string bad_target_path_string(stream.str()); - bad_target_path_string[0x15] = '\0'; + bad_target_path_string[0x18] = '\0'; std::stringstream bad_target_path_stream(bad_target_path_string); std::unique_ptr<const IdmapHeader> bad_target_path_header = IdmapHeader::FromBinaryStream(bad_target_path_stream); @@ -490,9 +506,9 @@ TEST(IdmapTests, IdmapHeaderIsUpToDate) { ASSERT_FALSE(bad_magic_header->IsUpToDate(target_apk_path.c_str(), overlay_apk_path.c_str(), PolicyFlags::PUBLIC, /* enforce_overlayable */ true)); - // overlay path: bytes (0x115, 0x214) + // overlay path: bytes (0x118, 0x217) std::string bad_overlay_path_string(stream.str()); - bad_overlay_path_string[0x115] = '\0'; + bad_overlay_path_string[0x118] = '\0'; std::stringstream bad_overlay_path_stream(bad_overlay_path_string); std::unique_ptr<const IdmapHeader> bad_overlay_path_header = IdmapHeader::FromBinaryStream(bad_overlay_path_stream); diff --git a/cmds/idmap2/tests/PrettyPrintVisitorTests.cpp b/cmds/idmap2/tests/PrettyPrintVisitorTests.cpp index 9a10079772bf..d30fbfcb1d3c 100644 --- a/cmds/idmap2/tests/PrettyPrintVisitorTests.cpp +++ b/cmds/idmap2/tests/PrettyPrintVisitorTests.cpp @@ -56,7 +56,8 @@ TEST(PrettyPrintVisitorTests, CreatePrettyPrintVisitor) { ASSERT_NE(stream.str().find("target apk path : "), std::string::npos); ASSERT_NE(stream.str().find("overlay apk path : "), std::string::npos); - ASSERT_NE(stream.str().find(R::target::integer::literal::int1 + " -> 0x7f010000 integer/int1\n"), + ASSERT_NE(stream.str().find(R::target::integer::literal::int1 + + " -> 0x7f010000 (integer/int1 -> integer/int1)\n"), std::string::npos); } @@ -75,7 +76,7 @@ TEST(PrettyPrintVisitorTests, CreatePrettyPrintVisitorWithoutAccessToApks) { ASSERT_NE(stream.str().find("target apk path : "), std::string::npos); ASSERT_NE(stream.str().find("overlay apk path : "), std::string::npos); - ASSERT_NE(stream.str().find("0x7f020000 -> 0x7f020000\n"), std::string::npos); + ASSERT_NE(stream.str().find("0x7f020000 -> 0x7f020000 (\?\?\? -> \?\?\?)\n"), std::string::npos); } } // namespace android::idmap2 diff --git a/cmds/idmap2/tests/RawPrintVisitorTests.cpp b/cmds/idmap2/tests/RawPrintVisitorTests.cpp index b268d5add141..95bd94733ab3 100644 --- a/cmds/idmap2/tests/RawPrintVisitorTests.cpp +++ b/cmds/idmap2/tests/RawPrintVisitorTests.cpp @@ -65,7 +65,7 @@ TEST(RawPrintVisitorTests, CreateRawPrintVisitor) { (*idmap)->accept(&visitor); ASSERT_CONTAINS_REGEX(ADDRESS "504d4449 magic\n", stream.str()); - ASSERT_CONTAINS_REGEX(ADDRESS "00000004 version\n", stream.str()); + ASSERT_CONTAINS_REGEX(ADDRESS "00000005 version\n", stream.str()); ASSERT_CONTAINS_REGEX( StringPrintf(ADDRESS "%s target crc\n", android::idmap2::TestConstants::TARGET_CRC_STRING), stream.str()); @@ -73,19 +73,19 @@ TEST(RawPrintVisitorTests, CreateRawPrintVisitor) { StringPrintf(ADDRESS "%s overlay crc\n", android::idmap2::TestConstants::OVERLAY_CRC_STRING), stream.str()); ASSERT_CONTAINS_REGEX(ADDRESS "00000001 fulfilled policies: public\n", stream.str()); - ASSERT_CONTAINS_REGEX(ADDRESS " 01 enforce overlayable\n", stream.str()); + ASSERT_CONTAINS_REGEX(ADDRESS "00000001 enforce overlayable\n", stream.str()); ASSERT_CONTAINS_REGEX(ADDRESS " 7f target package id\n", stream.str()); ASSERT_CONTAINS_REGEX(ADDRESS " 7f overlay package id\n", stream.str()); ASSERT_CONTAINS_REGEX(ADDRESS "00000004 target entry count\n", stream.str()); ASSERT_CONTAINS_REGEX(ADDRESS "00000004 overlay entry count\n", stream.str()); ASSERT_CONTAINS_REGEX(ADDRESS "00000004 overlay entry count\n", stream.str()); ASSERT_CONTAINS_REGEX(ADDRESS "00000008 string pool index offset\n", stream.str()); - ASSERT_CONTAINS_REGEX(ADDRESS "000000b4 string pool byte length\n", stream.str()); ASSERT_CONTAINS_REGEX(ADDRESS "7f010000 target id: integer/int1\n", stream.str()); - ASSERT_CONTAINS_REGEX(ADDRESS " 07 type: reference \\(dynamic\\)\n", stream.str()); - ASSERT_CONTAINS_REGEX(ADDRESS "7f010000 value: integer/int1\n", stream.str()); + ASSERT_CONTAINS_REGEX(ADDRESS "7f010000 overlay id: integer/int1\n", stream.str()); ASSERT_CONTAINS_REGEX(ADDRESS "7f010000 overlay id: integer/int1\n", stream.str()); ASSERT_CONTAINS_REGEX(ADDRESS "7f010000 target id: integer/int1\n", stream.str()); + ASSERT_CONTAINS_REGEX(ADDRESS "000000b4 string pool size\n", stream.str()); + ASSERT_CONTAINS_REGEX("000002bc: ........ string pool: ...\n", stream.str()); } TEST(RawPrintVisitorTests, CreateRawPrintVisitorWithoutAccessToApks) { @@ -102,22 +102,26 @@ TEST(RawPrintVisitorTests, CreateRawPrintVisitorWithoutAccessToApks) { (*idmap)->accept(&visitor); ASSERT_CONTAINS_REGEX(ADDRESS "504d4449 magic\n", stream.str()); - ASSERT_CONTAINS_REGEX(ADDRESS "00000004 version\n", stream.str()); + ASSERT_CONTAINS_REGEX(ADDRESS "00000005 version\n", stream.str()); ASSERT_CONTAINS_REGEX(ADDRESS "00001234 target crc\n", stream.str()); ASSERT_CONTAINS_REGEX(ADDRESS "00005678 overlay crc\n", stream.str()); ASSERT_CONTAINS_REGEX(ADDRESS "00000011 fulfilled policies: public|signature\n", stream.str()); - ASSERT_CONTAINS_REGEX(ADDRESS " 01 enforce overlayable\n", stream.str()); + ASSERT_CONTAINS_REGEX(ADDRESS "00000001 enforce overlayable\n", stream.str()); ASSERT_CONTAINS_REGEX(ADDRESS " 7f target package id\n", stream.str()); ASSERT_CONTAINS_REGEX(ADDRESS " 7f overlay package id\n", stream.str()); ASSERT_CONTAINS_REGEX(ADDRESS "00000003 target entry count\n", stream.str()); + ASSERT_CONTAINS_REGEX(ADDRESS "00000001 target inline entry count\n", stream.str()); ASSERT_CONTAINS_REGEX(ADDRESS "00000003 overlay entry count\n", stream.str()); ASSERT_CONTAINS_REGEX(ADDRESS "00000000 string pool index offset\n", stream.str()); - ASSERT_CONTAINS_REGEX(ADDRESS "00000000 string pool byte length\n", stream.str()); ASSERT_CONTAINS_REGEX(ADDRESS "7f020000 target id\n", stream.str()); - ASSERT_CONTAINS_REGEX(ADDRESS " 01 type: reference\n", stream.str()); - ASSERT_CONTAINS_REGEX(ADDRESS "7f020000 value\n", stream.str()); ASSERT_CONTAINS_REGEX(ADDRESS "7f020000 overlay id\n", stream.str()); ASSERT_CONTAINS_REGEX(ADDRESS "7f020000 target id\n", stream.str()); + ASSERT_CONTAINS_REGEX(ADDRESS " 11 type: integer\n", stream.str()); + ASSERT_CONTAINS_REGEX(ADDRESS "12345678 data\n", stream.str()); + ASSERT_CONTAINS_REGEX(ADDRESS "7f020000 overlay id\n", stream.str()); + ASSERT_CONTAINS_REGEX(ADDRESS "7f030002 target id\n", stream.str()); + ASSERT_CONTAINS_REGEX(ADDRESS "00000004 string pool size\n", stream.str()); + ASSERT_CONTAINS_REGEX("00000278: ........ string pool: ...\n", stream.str()); } } // namespace android::idmap2 diff --git a/cmds/idmap2/tests/ResourceMappingTests.cpp b/cmds/idmap2/tests/ResourceMappingTests.cpp index 3ec6ac24b238..185e9292346d 100644 --- a/cmds/idmap2/tests/ResourceMappingTests.cpp +++ b/cmds/idmap2/tests/ResourceMappingTests.cpp @@ -77,30 +77,61 @@ Result<ResourceMapping> TestGetResourceMapping(const android::StringPiece& local fulfilled_policies, enforce_overlayable); } -Result<Unit> MappingExists(const ResourceMapping& mapping, const ResourceId& target_resource, - const uint8_t type, const uint32_t value, bool rewrite) { +Result<Unit> MappingExists(const ResourceMapping& mapping, ResourceId target_resource, + ResourceId overlay_resource, bool rewrite) { auto target_map = mapping.GetTargetToOverlayMap(); auto entry_map = target_map.find(target_resource); if (entry_map == target_map.end()) { return Error("Failed to find mapping for target resource"); } - if (entry_map->second.data_type != type) { - return Error(R"(Expected type: "0x%02x" Actual type: "0x%02x")", type, - entry_map->second.data_type); + auto actual_overlay_resource = std::get_if<ResourceId>(&entry_map->second); + if (actual_overlay_resource == nullptr) { + return Error("Target resource is not mapped to an overlay resource id"); } - if (entry_map->second.data_value != value) { - return Error(R"(Expected value: "0x%08x" Actual value: "0x%08x")", type, - entry_map->second.data_value); + if (*actual_overlay_resource != overlay_resource) { + return Error(R"(Expected id: "0x%02x" Actual id: "0x%02x")", overlay_resource, + *actual_overlay_resource); } auto overlay_map = mapping.GetOverlayToTargetMap(); - auto overlay_iter = overlay_map.find(entry_map->second.data_value); + auto overlay_iter = overlay_map.find(overlay_resource); if ((overlay_iter != overlay_map.end()) != rewrite) { return Error(R"(Expected rewriting: "%s")", rewrite ? "true" : "false"); } + if (rewrite && overlay_iter->second != target_resource) { + return Error(R"(Expected rewrite id: "0x%02x" Actual id: "0x%02x")", target_resource, + overlay_iter->second); + } + + return Result<Unit>({}); +} + +Result<Unit> MappingExists(const ResourceMapping& mapping, const ResourceId& target_resource, + const uint8_t type, const uint32_t value) { + auto target_map = mapping.GetTargetToOverlayMap(); + auto entry_map = target_map.find(target_resource); + if (entry_map == target_map.end()) { + return Error("Failed to find mapping for target resource"); + } + + auto actual_overlay_value = std::get_if<TargetValue>(&entry_map->second); + if (actual_overlay_value == nullptr) { + return Error("Target resource is not mapped to an inline value"); + } + + if (actual_overlay_value->data_type != type) { + return Error(R"(Expected type: "0x%02x" Actual type: "0x%02x")", type, + actual_overlay_value->data_type); + } + + if (actual_overlay_value->data_value != value) { + return Error(R"(Expected value: "0x%08x" Actual value: "0x%08x")", type, + actual_overlay_value->data_value); + } + return Result<Unit>({}); } @@ -116,14 +147,14 @@ TEST(ResourceMappingTests, ResourcesFromApkAssetsLegacy) { ASSERT_TRUE(resources) << resources.GetErrorMessage(); auto& res = *resources; ASSERT_EQ(res.GetTargetToOverlayMap().size(), 4U); - ASSERT_RESULT(MappingExists(res, R::target::integer::int1, Res_value::TYPE_REFERENCE, - R::overlay::integer::int1, false /* rewrite */)); - ASSERT_RESULT(MappingExists(res, R::target::string::str1, Res_value::TYPE_REFERENCE, - R::overlay::string::str1, false /* rewrite */)); - ASSERT_RESULT(MappingExists(res, R::target::string::str3, Res_value::TYPE_REFERENCE, - R::overlay::string::str3, false /* rewrite */)); - ASSERT_RESULT(MappingExists(res, R::target::string::str4, Res_value::TYPE_REFERENCE, - R::overlay::string::str4, false /* rewrite */)); + ASSERT_RESULT( + MappingExists(res, R::target::integer::int1, R::overlay::integer::int1, false /* rewrite */)); + ASSERT_RESULT( + MappingExists(res, R::target::string::str1, R::overlay::string::str1, false /* rewrite */)); + ASSERT_RESULT( + MappingExists(res, R::target::string::str3, R::overlay::string::str3, false /* rewrite */)); + ASSERT_RESULT( + MappingExists(res, R::target::string::str4, R::overlay::string::str4, false /* rewrite */)); } TEST(ResourceMappingTests, ResourcesFromApkAssetsNonMatchingNames) { @@ -138,12 +169,12 @@ TEST(ResourceMappingTests, ResourcesFromApkAssetsNonMatchingNames) { ASSERT_TRUE(resources) << resources.GetErrorMessage(); auto& res = *resources; ASSERT_EQ(res.GetTargetToOverlayMap().size(), 3U); - ASSERT_RESULT(MappingExists(res, R::target::string::str1, Res_value::TYPE_DYNAMIC_REFERENCE, - R::overlay::string::str4, true /* rewrite */)); - ASSERT_RESULT(MappingExists(res, R::target::string::str3, Res_value::TYPE_DYNAMIC_REFERENCE, - R::overlay::string::str1, true /* rewrite */)); - ASSERT_RESULT(MappingExists(res, R::target::string::str4, Res_value::TYPE_DYNAMIC_REFERENCE, - R::overlay::string::str3, true /* rewrite */)); + ASSERT_RESULT( + MappingExists(res, R::target::string::str1, R::overlay::string::str4, true /* rewrite */)); + ASSERT_RESULT( + MappingExists(res, R::target::string::str3, R::overlay::string::str1, true /* rewrite */)); + ASSERT_RESULT( + MappingExists(res, R::target::string::str4, R::overlay::string::str3, true /* rewrite */)); } TEST(ResourceMappingTests, DoNotRewriteNonOverlayResourceId) { @@ -159,10 +190,9 @@ TEST(ResourceMappingTests, DoNotRewriteNonOverlayResourceId) { auto& res = *resources; ASSERT_EQ(res.GetTargetToOverlayMap().size(), 2U); ASSERT_EQ(res.GetOverlayToTargetMap().size(), 1U); - ASSERT_RESULT(MappingExists(res, R::target::string::str1, Res_value::TYPE_REFERENCE, 0x0104000a, + ASSERT_RESULT(MappingExists(res, R::target::string::str1, 0x0104000a, false /* rewrite */)); // -> android:string/ok - ASSERT_RESULT(MappingExists(res, R::target::string::str3, Res_value::TYPE_DYNAMIC_REFERENCE, - 0x7f020001, true /* rewrite */)); + ASSERT_RESULT(MappingExists(res, R::target::string::str3, 0x7f020001, true /* rewrite */)); } TEST(ResourceMappingTests, InlineResources) { @@ -180,10 +210,8 @@ TEST(ResourceMappingTests, InlineResources) { ASSERT_EQ(res.GetTargetToOverlayMap().size(), 2U); ASSERT_EQ(res.GetOverlayToTargetMap().size(), 0U); ASSERT_RESULT(MappingExists(res, R::target::string::str1, Res_value::TYPE_STRING, - overlay_string_pool_size + 0U, - false /* rewrite */)); // -> "Hello World" - ASSERT_RESULT(MappingExists(res, R::target::integer::int1, Res_value::TYPE_INT_DEC, 73U, - false /* rewrite */)); // -> 73 + overlay_string_pool_size + 0U)); // -> "Hello World" + ASSERT_RESULT(MappingExists(res, R::target::integer::int1, Res_value::TYPE_INT_DEC, 73U)); } TEST(ResourceMappingTests, CreateIdmapFromApkAssetsPolicySystemPublic) { @@ -195,13 +223,13 @@ TEST(ResourceMappingTests, CreateIdmapFromApkAssetsPolicySystemPublic) { ASSERT_TRUE(resources) << resources.GetErrorMessage(); auto& res = *resources; ASSERT_EQ(res.GetTargetToOverlayMap().size(), 3U); - ASSERT_RESULT(MappingExists(res, R::target::string::policy_public, Res_value::TYPE_REFERENCE, + ASSERT_RESULT(MappingExists(res, R::target::string::policy_public, R::system_overlay::string::policy_public, false /* rewrite */)); - ASSERT_RESULT(MappingExists(res, R::target::string::policy_system, Res_value::TYPE_REFERENCE, + ASSERT_RESULT(MappingExists(res, R::target::string::policy_system, R::system_overlay::string::policy_system, false /* rewrite */)); - ASSERT_RESULT( - MappingExists(res, R::target::string::policy_system_vendor, Res_value::TYPE_REFERENCE, - R::system_overlay::string::policy_system_vendor, false /* rewrite */)); + ASSERT_RESULT(MappingExists(res, R::target::string::policy_system_vendor, + R::system_overlay::string::policy_system_vendor, + false /* rewrite */)); } // Resources that are not declared as overlayable and resources that a protected by policies the @@ -215,15 +243,15 @@ TEST(ResourceMappingTests, CreateIdmapFromApkAssetsPolicySystemPublicInvalid) { ASSERT_TRUE(resources) << resources.GetErrorMessage(); auto& res = *resources; ASSERT_EQ(res.GetTargetToOverlayMap().size(), 3U); - ASSERT_RESULT(MappingExists(res, R::target::string::policy_public, Res_value::TYPE_REFERENCE, + ASSERT_RESULT(MappingExists(res, R::target::string::policy_public, R::system_overlay_invalid::string::policy_public, false /* rewrite */)); - ASSERT_RESULT(MappingExists(res, R::target::string::policy_system, Res_value::TYPE_REFERENCE, + ASSERT_RESULT(MappingExists(res, R::target::string::policy_system, R::system_overlay_invalid::string::policy_system, false /* rewrite */)); - ASSERT_RESULT( - MappingExists(res, R::target::string::policy_system_vendor, Res_value::TYPE_REFERENCE, - R::system_overlay_invalid::string::policy_system_vendor, false /* rewrite */)); + ASSERT_RESULT(MappingExists(res, R::target::string::policy_system_vendor, + R::system_overlay_invalid::string::policy_system_vendor, + false /* rewrite */)); } // Resources that are not declared as overlayable and resources that a protected by policies the @@ -238,37 +266,36 @@ TEST(ResourceMappingTests, ResourcesFromApkAssetsPolicySystemPublicInvalidIgnore ASSERT_TRUE(resources) << resources.GetErrorMessage(); auto& res = *resources; ASSERT_EQ(res.GetTargetToOverlayMap().size(), 11U); - ASSERT_RESULT(MappingExists(res, R::target::string::not_overlayable, Res_value::TYPE_REFERENCE, + ASSERT_RESULT(MappingExists(res, R::target::string::not_overlayable, R::system_overlay_invalid::string::not_overlayable, false /* rewrite */)); - ASSERT_RESULT(MappingExists(res, R::target::string::other, Res_value::TYPE_REFERENCE, + ASSERT_RESULT(MappingExists(res, R::target::string::other, R::system_overlay_invalid::string::other, false /* rewrite */)); - ASSERT_RESULT(MappingExists(res, R::target::string::policy_actor, Res_value::TYPE_REFERENCE, + ASSERT_RESULT(MappingExists(res, R::target::string::policy_actor, R::system_overlay_invalid::string::policy_actor, false /* rewrite */)); - ASSERT_RESULT(MappingExists(res, R::target::string::policy_odm, Res_value::TYPE_REFERENCE, + ASSERT_RESULT(MappingExists(res, R::target::string::policy_odm, R::system_overlay_invalid::string::policy_odm, false /* rewrite */)); - ASSERT_RESULT(MappingExists(res, R::target::string::policy_oem, Res_value::TYPE_REFERENCE, + ASSERT_RESULT(MappingExists(res, R::target::string::policy_oem, R::system_overlay_invalid::string::policy_oem, false /* rewrite */)); - ASSERT_RESULT(MappingExists(res, R::target::string::policy_product, Res_value::TYPE_REFERENCE, + ASSERT_RESULT(MappingExists(res, R::target::string::policy_product, R::system_overlay_invalid::string::policy_product, false /* rewrite */)); - ASSERT_RESULT(MappingExists(res, R::target::string::policy_public, Res_value::TYPE_REFERENCE, + ASSERT_RESULT(MappingExists(res, R::target::string::policy_public, R::system_overlay_invalid::string::policy_public, false /* rewrite */)); ASSERT_RESULT(MappingExists(res, R::target::string::policy_config_signature, - Res_value::TYPE_REFERENCE, R::system_overlay_invalid::string::policy_config_signature, false /* rewrite */)); - ASSERT_RESULT(MappingExists(res, R::target::string::policy_signature, Res_value::TYPE_REFERENCE, + ASSERT_RESULT(MappingExists(res, R::target::string::policy_signature, R::system_overlay_invalid::string::policy_signature, false /* rewrite */)); - ASSERT_RESULT(MappingExists(res, R::target::string::policy_system, Res_value::TYPE_REFERENCE, + ASSERT_RESULT(MappingExists(res, R::target::string::policy_system, R::system_overlay_invalid::string::policy_system, false /* rewrite */)); - ASSERT_RESULT( - MappingExists(res, R::target::string::policy_system_vendor, Res_value::TYPE_REFERENCE, - R::system_overlay_invalid::string::policy_system_vendor, false /* rewrite */)); + ASSERT_RESULT(MappingExists(res, R::target::string::policy_system_vendor, + R::system_overlay_invalid::string::policy_system_vendor, + false /* rewrite */)); } // Overlays that do not target an <overlayable> tag can overlay resources defined within any @@ -281,14 +308,14 @@ TEST(ResourceMappingTests, ResourcesFromApkAssetsNoDefinedOverlayableAndNoTarget ASSERT_TRUE(resources) << resources.GetErrorMessage(); auto& res = *resources; ASSERT_EQ(res.GetTargetToOverlayMap().size(), 4U); - ASSERT_RESULT(MappingExists(res, R::target::integer::int1, Res_value::TYPE_REFERENCE, - R::overlay::integer::int1, false /* rewrite */)); - ASSERT_RESULT(MappingExists(res, R::target::string::str1, Res_value::TYPE_REFERENCE, - R::overlay::string::str1, false /* rewrite */)); - ASSERT_RESULT(MappingExists(res, R::target::string::str3, Res_value::TYPE_REFERENCE, - R::overlay::string::str3, false /* rewrite */)); - ASSERT_RESULT(MappingExists(res, R::target::string::str4, Res_value::TYPE_REFERENCE, - R::overlay::string::str4, false /* rewrite */)); + ASSERT_RESULT( + MappingExists(res, R::target::integer::int1, R::overlay::integer::int1, false /* rewrite */)); + ASSERT_RESULT( + MappingExists(res, R::target::string::str1, R::overlay::string::str1, false /* rewrite */)); + ASSERT_RESULT( + MappingExists(res, R::target::string::str3, R::overlay::string::str3, false /* rewrite */)); + ASSERT_RESULT( + MappingExists(res, R::target::string::str4, R::overlay::string::str4, false /* rewrite */)); } // Overlays that are neither pre-installed nor signed with the same signature as the target cannot @@ -302,9 +329,9 @@ TEST(ResourceMappingTests, ResourcesFromApkAssetsDefaultPoliciesPublicFail) { ASSERT_EQ(resources->GetTargetToOverlayMap().size(), 0U); } -// Overlays that are pre-installed or are signed with the same signature as the target or are signed -// with the same signature as the reference package can overlay packages that have not defined -// overlayable resources. +// Overlays that are pre-installed or are signed with the same signature as the target or are +// signed with the same signature as the reference package can overlay packages that have not +// defined overlayable resources. TEST(ResourceMappingTests, ResourcesFromApkAssetsDefaultPolicies) { auto CheckEntries = [&](const PolicyBitmask& fulfilled_policies) -> void { auto resources = TestGetResourceMapping("/target/target-no-overlayable.apk", @@ -315,39 +342,38 @@ TEST(ResourceMappingTests, ResourcesFromApkAssetsDefaultPolicies) { ASSERT_TRUE(resources) << resources.GetErrorMessage(); auto& res = *resources; ASSERT_EQ(resources->GetTargetToOverlayMap().size(), 11U); - ASSERT_RESULT(MappingExists(res, R::target::string::not_overlayable, Res_value::TYPE_REFERENCE, + ASSERT_RESULT(MappingExists(res, R::target::string::not_overlayable, R::system_overlay_invalid::string::not_overlayable, false /* rewrite */)); - ASSERT_RESULT(MappingExists(res, R::target::string::other, Res_value::TYPE_REFERENCE, + ASSERT_RESULT(MappingExists(res, R::target::string::other, R::system_overlay_invalid::string::other, false /* rewrite */)); - ASSERT_RESULT(MappingExists(res, R::target::string::policy_actor, Res_value::TYPE_REFERENCE, + ASSERT_RESULT(MappingExists(res, R::target::string::policy_actor, R::system_overlay_invalid::string::policy_actor, false /* rewrite */)); - ASSERT_RESULT(MappingExists(res, R::target::string::policy_odm, Res_value::TYPE_REFERENCE, + ASSERT_RESULT(MappingExists(res, R::target::string::policy_odm, R::system_overlay_invalid::string::policy_odm, false /* rewrite */)); - ASSERT_RESULT(MappingExists(res, R::target::string::policy_oem, Res_value::TYPE_REFERENCE, + ASSERT_RESULT(MappingExists(res, R::target::string::policy_oem, R::system_overlay_invalid::string::policy_oem, false /* rewrite */)); - ASSERT_RESULT(MappingExists(res, R::target::string::policy_product, Res_value::TYPE_REFERENCE, + ASSERT_RESULT(MappingExists(res, R::target::string::policy_product, R::system_overlay_invalid::string::policy_product, false /* rewrite */)); - ASSERT_RESULT(MappingExists(res, R::target::string::policy_public, Res_value::TYPE_REFERENCE, + ASSERT_RESULT(MappingExists(res, R::target::string::policy_public, R::system_overlay_invalid::string::policy_public, false /* rewrite */)); ASSERT_RESULT(MappingExists(res, R::target::string::policy_config_signature, - Res_value::TYPE_REFERENCE, R::system_overlay_invalid::string::policy_config_signature, false /* rewrite */)); - ASSERT_RESULT(MappingExists(res, R::target::string::policy_signature, Res_value::TYPE_REFERENCE, + ASSERT_RESULT(MappingExists(res, R::target::string::policy_signature, R::system_overlay_invalid::string::policy_signature, false /* rewrite */)); - ASSERT_RESULT(MappingExists(res, R::target::string::policy_system, Res_value::TYPE_REFERENCE, + ASSERT_RESULT(MappingExists(res, R::target::string::policy_system, R::system_overlay_invalid::string::policy_system, false /* rewrite */)); - ASSERT_RESULT(MappingExists( - res, R::target::string::policy_system_vendor, Res_value::TYPE_REFERENCE, - R::system_overlay_invalid::string::policy_system_vendor, false /* rewrite */)); + ASSERT_RESULT(MappingExists(res, R::target::string::policy_system_vendor, + R::system_overlay_invalid::string::policy_system_vendor, + false /* rewrite */)); }; CheckEntries(PolicyFlags::SIGNATURE); diff --git a/cmds/idmap2/tests/TestConstants.h b/cmds/idmap2/tests/TestConstants.h index 9641f6b55670..69575b8f9c01 100644 --- a/cmds/idmap2/tests/TestConstants.h +++ b/cmds/idmap2/tests/TestConstants.h @@ -19,7 +19,7 @@ namespace android::idmap2::TestConstants { -constexpr const auto TARGET_CRC = 0x7c2d4719; +constexpr const auto TARGET_CRC = 0x7c2d4719; constexpr const auto TARGET_CRC_STRING = "7c2d4719"; constexpr const auto OVERLAY_CRC = 0x5afff726; diff --git a/cmds/idmap2/tests/TestHelpers.h b/cmds/idmap2/tests/TestHelpers.h index b599dcb0069a..d0a8e3db8eca 100644 --- a/cmds/idmap2/tests/TestHelpers.h +++ b/cmds/idmap2/tests/TestHelpers.h @@ -30,7 +30,7 @@ const unsigned char idmap_raw_data[] = { 0x49, 0x44, 0x4d, 0x50, // 0x4: version - 0x04, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, // 0x8: target crc 0x34, 0x12, 0x00, 0x00, @@ -42,9 +42,9 @@ const unsigned char idmap_raw_data[] = { 0x11, 0x00, 0x00, 0x00, // 0x14: enforce overlayable - 0x01, + 0x01, 0x00, 0x00, 0x00, - // 0x15: target path "targetX.apk" + // 0x18: target path "targetX.apk" 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x58, 0x2e, 0x61, 0x70, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -62,7 +62,7 @@ const unsigned char idmap_raw_data[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - // 0x115: overlay path "overlayX.apk" + // 0x118: overlay path "overlayX.apk" 0x6f, 0x76, 0x65, 0x72, 0x6c, 0x61, 0x79, 0x58, 0x2e, 0x61, 0x70, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -80,71 +80,89 @@ const unsigned char idmap_raw_data[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - // 0x215: debug string - // string length, including terminating null - 0x08, 0x00, 0x00, 0x00, + // 0x218: debug string + // string length, + 0x05, 0x00, 0x00, 0x00, - // string contents "debug\0\0\0" (padded to word alignment) + // 0x21c string contents "debug\0\0\0" (padded to word alignment) 0x64, 0x65, 0x62, 0x75, 0x67, 0x00, 0x00, 0x00, // DATA HEADER - // 0x221: target_package_id + // 0x224: target_package_id 0x7f, - // 0x222: overlay_package_id + // 0x225: overlay_package_id 0x7f, - // 0x223: target_entry_count - 0x03, 0x00, 0x00, 0x00, + // 0x226: padding + 0x00, 0x00, - // 0x227: overlay_entry_count + // 0x228: target_entry_count 0x03, 0x00, 0x00, 0x00, - // 0x22b: string_pool_offset - 0x00, 0x00, 0x00, 0x00, + // 0x22c: target_inline_entry_count + 0x01, 0x00, 0x00, 0x00, + + // 0x230: overlay_entry_count + 0x03, 0x00, 0x00, 0x00, - // 0x22f: string_pool_byte_length + // 0x234: string_pool_offset 0x00, 0x00, 0x00, 0x00, // TARGET ENTRIES - // 0x233: 0x7f020000 + // 0x238: target id (0x7f020000) 0x00, 0x00, 0x02, 0x7f, - // 0x237: TYPE_REFERENCE - 0x01, - - // 0x238: 0x7f020000 + // 0x23c: overlay_id (0x7f020000) 0x00, 0x00, 0x02, 0x7f, - // 0x23c: 0x7f030000 + // 0x240: target id (0x7f030000) 0x00, 0x00, 0x03, 0x7f, - // 0x240: TYPE_REFERENCE - 0x01, - - // 0x241: 0x7f030000 + // 0x244: overlay_id (0x7f030000) 0x00, 0x00, 0x03, 0x7f, - // 0x245: 0x7f030002 + // 0x248: target id (0x7f030002) 0x02, 0x00, 0x03, 0x7f, - // 0x249: TYPE_REFERENCE - 0x01, - - // 0x24a: 0x7f030001 + // 0x24c: overlay_id (0x7f030001) 0x01, 0x00, 0x03, 0x7f, + // INLINE TARGET ENTRIES + + // 0x250: target_id + 0x00, 0x00, 0x04, 0x7f, + + // 0x254: Res_value::size (value ignored by idmap) + 0x08, 0x00, + + // 0x256: Res_value::res0 (value ignored by idmap) + 0x00, + + // 0x257: Res_value::dataType (TYPE_INT_HEX) + 0x11, + + // 0x258: Res_value::data + 0x78, 0x56, 0x34, 0x12, + // OVERLAY ENTRIES - // 0x24e: 0x7f020000 -> 0x7f020000 + // 0x25c: 0x7f020000 -> 0x7f020000 0x00, 0x00, 0x02, 0x7f, 0x00, 0x00, 0x02, 0x7f, - // 0x256: 0x7f030000 -> 0x7f030000 + // 0x264: 0x7f030000 -> 0x7f030000 0x00, 0x00, 0x03, 0x7f, 0x00, 0x00, 0x03, 0x7f, - // 0x25e: 0x7f030001 -> 0x7f030002 - 0x01, 0x00, 0x03, 0x7f, 0x02, 0x00, 0x03, 0x7f}; + // 0x26c: 0x7f030001 -> 0x7f030002 + 0x01, 0x00, 0x03, 0x7f, 0x02, 0x00, 0x03, 0x7f, + + // 0x274: string pool + // string length, + 0x04, 0x00, 0x00, 0x00, + + // 0x278 string contents "test" (padded to word alignment) + 0x74, 0x65, 0x73, 0x74}; -const unsigned int idmap_raw_data_len = 0x266; +const unsigned int idmap_raw_data_len = 0x27c; std::string GetTestDataPath(); diff --git a/cmds/statsd/Android.bp b/cmds/statsd/Android.bp index 88db1d84df8e..012450d73266 100644 --- a/cmds/statsd/Android.bp +++ b/cmds/statsd/Android.bp @@ -430,6 +430,30 @@ java_library { }, } +java_library { + name: "statsdprotonano", + sdk_version: "9", + proto: { + type: "nano", + output_params: ["store_unknown_fields=true"], + include_dirs: ["external/protobuf/src"], + }, + srcs: [ + "src/atoms.proto", + "src/shell/shell_config.proto", + "src/shell/shell_data.proto", + "src/stats_log.proto", + "src/statsd_config.proto", + ], + static_libs: [ + "platformprotosnano", + ], + // Protos have lots of MissingOverride and similar. + errorprone: { + javacflags: ["-XepDisableAllChecks"], + }, +} + // Filegroup for statsd config proto definition. filegroup { name: "statsd-config-proto-def", diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto index e5dbb4239542..3d67e6574652 100644 --- a/cmds/statsd/src/atoms.proto +++ b/cmds/statsd/src/atoms.proto @@ -495,13 +495,16 @@ message Atom { HdmiCecMessageReported hdmi_cec_message_reported = 310 [(module) = "framework"]; AirplaneMode airplane_mode = 311 [(module) = "telephony"]; ModemRestart modem_restart = 312 [(module) = "telephony"]; + CarrierIdMismatchEvent carrier_id_mismatch_event = 313 [(module) = "telephony"]; + CarrierIdMatchingTable carrier_id_table_update = 314 [(module) = "telephony"]; + DataStallRecoveryReported data_stall_recovery_reported = 315 [(module) = "telephony"]; // StatsdStats tracks platform atoms with ids upto 500. // Update StatsdStats::kMaxPushedAtomId when atom ids here approach that value. } // Pulled events will start at field 10000. - // Next: 10088 + // Next: 10092 oneof pulled { WifiBytesTransfer wifi_bytes_transfer = 10000 [(module) = "framework"]; WifiBytesTransferByFgBg wifi_bytes_transfer_by_fg_bg = 10001 [(module) = "framework"]; @@ -602,6 +605,10 @@ message Atom { 10085 [(module) = "mediaprovider"]; IncomingSms incoming_sms = 10086 [(module) = "telephony"]; OutgoingSms outgoing_sms = 10087 [(module) = "telephony"]; + CarrierIdMatchingTable carrier_id_table_version = 10088 [(module) = "telephony"]; + DataCallSession data_call_session = 10089 [(module) = "telephony"]; + CellularServiceState cellular_service_state = 10090 [(module) = "telephony"]; + CellularDataServiceSwitch cellular_data_service_switch = 10091 [(module) = "telephony"]; } // DO NOT USE field numbers above 100,000 in AOSP. @@ -3834,6 +3841,12 @@ message AppStartFullyDrawn { // App startup time (until call to Activity#reportFullyDrawn()). optional int64 app_startup_time_millis = 6; + // The compiler filter used when when the package was optimized. + optional int32 package_optimization_compilation_filter = 7; + + // The reason why the package was optimized. + optional int32 package_optimization_compilation_reason = 8; + enum SourceType { UNAVAILABLE = 0; LAUNCHER = 1; @@ -3841,11 +3854,11 @@ message AppStartFullyDrawn { LOCKSCREEN = 3; } // The type of the startup source. - optional SourceType source_type = 7; + optional SourceType source_type = 9; // The time from the startup source to the beginning of handling the startup event. // -1 means not available. - optional int32 source_event_delay_millis = 8; + optional int32 source_event_delay_millis = 10; } /** @@ -10387,7 +10400,7 @@ message VoiceCallSession { // Number of other calls going on during call termination, for the same SIM slot. optional int32 concurrent_call_count_at_end = 14; - // Index of the SIM is used, 0 for single-SIM devices. + // Index of the SIM used, 0 for single-SIM devices. optional int32 sim_slot_index = 15; // Whether the device was in multi-SIM mode (with multiple active SIM profiles). @@ -10439,7 +10452,7 @@ message VoiceCallRatUsage { // Radio access technology. optional android.telephony.NetworkTypeEnum rat = 2; - // Total duration that voice calls spent on this carrier and RAT. + // Total duration that voice calls spent on this carrier and RAT, rounded to 5 minute. optional int64 total_duration_seconds = 3; // Total number of calls using this carrier and RAT. @@ -10448,6 +10461,82 @@ message VoiceCallRatUsage { } /** + * Pulls amount of time spend in each cellular service state. + * + * Each pull creates multiple atoms, one for each SIM slot/carrier/RAT(including ENDC), the order of + * which is irrelevant to time. If multi SIM settings changes during the period, durations will be + * counted separately before and after the change. Airplane mode does not count towards durations. + * + * Pulled from: + * frameworks/opt/telephony/src/java/com/android/internal/telephony/metrics/MetricsCollector.java + */ +message CellularServiceState { + // Radio access technology (RAT) for voice. + // NETWORK_TYPE_UNKNOWN when the device is out of service. + // NETWORK_TYPE_IWLAN when the device is using VoWiFi. + optional android.telephony.NetworkTypeEnum voice_rat = 1; + + // Radio access technology (RAT) for data. + // NETWORK_TYPE_UNKNOWN when the device is out of service. + // Only cellular RATs are valid and show where the device is camped. + optional android.telephony.NetworkTypeEnum data_rat = 2; + + // Whether the device was in roaming (domestic or international) for voice. + optional android.telephony.RoamingTypeEnum voice_roaming_type = 3; + + // Whether the device was in roaming (domestic or international) for data. + optional android.telephony.RoamingTypeEnum data_roaming_type = 4; + + // Whether the device is on LTE and has access to NR NSA, i.e. cell supports 5G (ENDC) and UE + // registration (attach/TAU) indicates ENDC is not restricted. + optional bool is_endc = 5; + + // Index of the SIM used, 0 for single-SIM devices. + optional int32 sim_slot_index = 6; + + // Whether the device was in multi-SIM mode (with multiple active SIM profiles). + optional bool is_multi_sim = 7; + + // Carrier ID of the SIM card. + // See https://source.android.com/devices/tech/config/carrierid. + optional int32 carrier_id = 8; + + // Total time spent in this service state, rounded to 5 minutes. + optional int32 total_time_seconds = 9; +} + +/** + * Pulls the number of times cellular data service state switches. + * + * Each pull creates multiple atoms, one for each RAT combination, the order of which is irrelevant + * to time. Switches for different SIM slots, carrier IDs, or multi-SIM settings are counted + * separately. + * + * Pulled from: + * frameworks/opt/telephony/src/java/com/android/internal/telephony/metrics/MetricsCollector.java + */ +message CellularDataServiceSwitch { + // Cellular RAT of the DATA domain from where the switch occurred. + optional android.telephony.NetworkTypeEnum rat_from = 1; + + // Cellular RAT of the DATA domain to where the switch occurred. + optional android.telephony.NetworkTypeEnum rat_to = 2; + + // Index of the SIM used, 0 for single-SIM devices. + optional int32 sim_slot_index = 3; + + // Whether the device was in multi-SIM mode (with multiple active SIM profiles). + optional bool is_multi_sim = 4; + + // Carrier ID of the SIM card. + // See https://source.android.com/devices/tech/config/carrierid. + optional int32 carrier_id = 5; + + // Number of switches from rat_from to rat_to. + optional int32 switch_count = 6; +} + +/** * Pulls the number of active SIM slots and SIMs/eSIM profiles. * * Pulled from: @@ -10516,7 +10605,7 @@ message IncomingSms { // Whether the SMS was received while roaming. optional bool is_roaming = 9; - // Index of the SIM is used, 0 for single-SIM devices. + // Index of the SIM used, 0 for single-SIM devices. optional int32 sim_slot_index = 10; // Whether the device was in multi-SIM mode (with multiple active SIM profiles). @@ -10568,7 +10657,7 @@ message OutgoingSms { // Whether the default SMS application generated the SMS (regardless of which application). optional bool is_from_default_app = 7; - // Index of the SIM is used, 0 for single-SIM devices. + // Index of the SIM used, 0 for single-SIM devices. optional int32 sim_slot_index = 8; // Whether the device was in multi-SIM mode (with multiple active SIM profiles). @@ -10626,6 +10715,141 @@ message ModemRestart { } /** + * Push the SIM card details when the carrier ID match is not complete. + * + * The atom is pushed when a SIM card is initialized and the MCC/MNC is not present in the + * carrier ID table, or the SIM card contains a GID1 value that is not present in the carrier ID + * table. This atom is pushed only once for each type of SIM card. + * + * Logged from: + * frameworks/opt/telephony/src/java/com/android/internal/telephony/metrics/CarrierIdMatchStats.java + */ +message CarrierIdMismatchEvent { + // Matched carrier ID. The value -1 is used if no match is found. + optional int32 carrier_id = 1; + + // MCC/MNC of the SIM card. + optional string mcc_mnc = 2; + + // Group identifier (level 1) of the SIM card. + optional string gid1 = 3; + + // SPN value of the SIM card. + optional string spn = 4; +} + +/** + * Pulls/pushes the version of the carrier ID matching table. + * + * The atom is pushed when a new version is detected. + * + * Logged from: + * frameworks/opt/telephony/src/java/com/android/internal/telephony/metrics/CarrierIdMatchStats.java + */ +message CarrierIdMatchingTable { + // Version of the CarrierId matching table. + optional int32 table_version = 1; +} + +/** + * Pulls information for a single data call session + * + * Each pull creates multiple atoms, one for each data call session. + * The sequence is randomized when pulled. + * + * Pulled from: + * frameworks/opt/telephony/src/java/com/android/internal/telephony/metrics/MetricsCollector.java + */ +message DataCallSession { + // A random number to be used as dimension to capture multiple atoms + optional int32 dimension = 1; + + // Whether the device was in multi-SIM mode (with multiple active SIM profiles). + optional bool is_multi_sim = 2; + + // Whether the call was made with an eSIM profile. + optional bool is_esim = 3; + + // Data profile of this call (for what purpose this call was made) + optional android.telephony.DataProfileEnum profile = 4; + + // APN type bitmask of the APN used: + // @ApnType in frameworks/base/telephony/java/android/telephony/Annotation.java. + optional int32 apn_type_bitmask = 5; + + // Carrier ID of the SIM + // See https://source.android.com/devices/tech/config/carrierid. + optional int32 carrier_id = 6; + + // Whether the subscription is roaming + optional bool is_roaming = 7; + + // Data RAT when the call ended, can be IWLAN for IMS/MMS, otherwise should be WWAN PS RAT. + // In the case that the connection hasn't ended yet, this field holds the current RAT. + // In the case the call ended due to Out Of Service (OOS), + // this field should be the last known RAT. + optional android.telephony.NetworkTypeEnum rat_at_end = 8; + + // Was the data call ended due to OOS + optional bool oos_at_end = 9; + + // Number of RAT switches during the data call + optional int64 rat_switch_count = 10; + + // Whether the call is on an opportunistic subscription + optional bool is_opportunistic = 11; + + // Packet data protocol used + optional android.telephony.ApnProtocolEnum ip_type = 12; + + // Whether the data call terminated before being established + optional bool setup_failed = 13; + + // Reason why the data call terminated, as in RIL_DataCallFailCause from ril.h + optional int32 failure_cause = 14; + + // Suggested retry back-off timer value from RIL + optional int32 suggested_retry_millis = 15; + + // Why the data call was deactivated + // Set by telephony for MO deactivations (unrelated to failure_cause) + optional android.telephony.DataDeactivateReasonEnum deactivate_reason = 16; + + // Duration of the data call, rounded into the closest 5 minutes. + optional int64 duration_minutes = 17; + + // Whether the data call is still connected when the atom is collected. + optional bool ongoing = 18; +} + +/** + * Logs data stall recovery event + * + * Logged from: + * frameworks/opt/telephony/src/java/com/android/internal/telephony/dataconnection/DcTracker.java + */ +message DataStallRecoveryReported { + // Carrier ID of the SIM + // See https://source.android.com/devices/tech/config/carrierid. + optional int32 carrier_id = 1; + + // Data RAT when the stall happened + optional android.telephony.NetworkTypeEnum rat = 2; + + // Signal strength when stall happened + optional android.telephony.SignalStrengthEnum signal_strength = 3; + + // Action taken to recover + optional android.telephony.DataStallRecoveryActionEnum action = 4; + + // Whether the subscription is opportunistic + optional bool is_opportunistic = 5; + + // Whether the device is in multi-SIM mode + optional bool is_multi_sim = 6; +} + +/** * Logs gnss stats from location service provider * * Pulled from: diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp index 3acafaa3560e..b2c0b32bf5ef 100644 --- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp +++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp @@ -17,14 +17,17 @@ #define DEBUG false #include "Log.h" + #include "DurationMetricProducer.h" -#include "guardrail/StatsdStats.h" -#include "stats_util.h" -#include "stats_log_util.h" #include <limits.h> #include <stdlib.h> +#include "guardrail/StatsdStats.h" +#include "metrics/parsing_utils/metrics_manager_util.h" +#include "stats_log_util.h" +#include "stats_util.h" + using android::util::FIELD_COUNT_REPEATED; using android::util::FIELD_TYPE_BOOL; using android::util::FIELD_TYPE_FLOAT; @@ -64,8 +67,8 @@ const int FIELD_ID_END_BUCKET_ELAPSED_MILLIS = 6; DurationMetricProducer::DurationMetricProducer( const ConfigKey& key, const DurationMetric& metric, const int conditionIndex, - const vector<ConditionState>& initialConditionCache, const size_t startIndex, - const size_t stopIndex, const size_t stopAllIndex, const bool nesting, + const vector<ConditionState>& initialConditionCache, const int startIndex, + const int stopIndex, const int stopAllIndex, const bool nesting, const sp<ConditionWizard>& wizard, const uint64_t protoHash, const FieldMatcher& internalDimensions, const int64_t timeBaseNs, const int64_t startTimeNs, const unordered_map<int, shared_ptr<Activation>>& eventActivationMap, @@ -143,6 +146,84 @@ DurationMetricProducer::~DurationMetricProducer() { VLOG("~DurationMetric() called"); } +bool DurationMetricProducer::onConfigUpdatedLocked( + const StatsdConfig& config, const int configIndex, const int metricIndex, + const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers, + const unordered_map<int64_t, int>& oldAtomMatchingTrackerMap, + const unordered_map<int64_t, int>& newAtomMatchingTrackerMap, + const sp<EventMatcherWizard>& matcherWizard, + const vector<sp<ConditionTracker>>& allConditionTrackers, + const unordered_map<int64_t, int>& conditionTrackerMap, const sp<ConditionWizard>& wizard, + const unordered_map<int64_t, int>& metricToActivationMap, + unordered_map<int, vector<int>>& trackerToMetricMap, + unordered_map<int, vector<int>>& conditionToMetricMap, + unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap, + unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap, + vector<int>& metricsWithActivation) { + if (!MetricProducer::onConfigUpdatedLocked( + config, configIndex, metricIndex, allAtomMatchingTrackers, + oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, matcherWizard, + allConditionTrackers, conditionTrackerMap, wizard, metricToActivationMap, + trackerToMetricMap, conditionToMetricMap, activationAtomTrackerToMetricMap, + deactivationAtomTrackerToMetricMap, metricsWithActivation)) { + return false; + } + + const DurationMetric& metric = config.duration_metric(configIndex); + const auto& what_it = conditionTrackerMap.find(metric.what()); + if (what_it == conditionTrackerMap.end()) { + ALOGE("DurationMetric's \"what\" is not present in the config"); + return false; + } + + const Predicate& durationWhat = config.predicate(what_it->second); + if (durationWhat.contents_case() != Predicate::ContentsCase::kSimplePredicate) { + ALOGE("DurationMetric's \"what\" must be a simple condition"); + return false; + } + + const SimplePredicate& simplePredicate = durationWhat.simple_predicate(); + + // Update indices: mStartIndex, mStopIndex, mStopAllIndex, mConditionIndex and MetricsManager + // maps. + if (!handleMetricWithAtomMatchingTrackers(simplePredicate.start(), metricIndex, + metric.has_dimensions_in_what(), + allAtomMatchingTrackers, newAtomMatchingTrackerMap, + trackerToMetricMap, mStartIndex)) { + ALOGE("Duration metrics must specify a valid start event matcher"); + return false; + } + + if (simplePredicate.has_stop() && + !handleMetricWithAtomMatchingTrackers(simplePredicate.stop(), metricIndex, + metric.has_dimensions_in_what(), + allAtomMatchingTrackers, newAtomMatchingTrackerMap, + trackerToMetricMap, mStopIndex)) { + return false; + } + + if (simplePredicate.has_stop_all() && + !handleMetricWithAtomMatchingTrackers(simplePredicate.stop_all(), metricIndex, + metric.has_dimensions_in_what(), + allAtomMatchingTrackers, newAtomMatchingTrackerMap, + trackerToMetricMap, mStopAllIndex)) { + return false; + } + + if (metric.has_condition() && + !handleMetricWithConditions(metric.condition(), metricIndex, conditionTrackerMap, + metric.links(), allConditionTrackers, mConditionTrackerIndex, + conditionToMetricMap)) { + return false; + } + + for (const auto& it : mCurrentSlicedDurationTrackerMap) { + it.second->onConfigUpdated(wizard, mConditionTrackerIndex); + } + + return true; +} + sp<AnomalyTracker> DurationMetricProducer::addAnomalyTracker( const Alert &alert, const sp<AlarmMonitor>& anomalyAlarmMonitor) { std::lock_guard<std::mutex> lock(mMutex); @@ -550,7 +631,7 @@ void DurationMetricProducer::onMatchedLogEventLocked(const size_t matcherIndex, } // Handles Stopall events. - if (matcherIndex == mStopAllIndex) { + if ((int)matcherIndex == mStopAllIndex) { for (auto& whatIt : mCurrentSlicedDurationTrackerMap) { whatIt.second->noteStopAll(event.GetElapsedTimestampNs()); } @@ -598,7 +679,7 @@ void DurationMetricProducer::onMatchedLogEventLocked(const size_t matcherIndex, } // Handles Stop events. - if (matcherIndex == mStopIndex) { + if ((int)matcherIndex == mStopIndex) { if (mUseWhatDimensionAsInternalDimension) { auto whatIt = mCurrentSlicedDurationTrackerMap.find(dimensionInWhat); if (whatIt != mCurrentSlicedDurationTrackerMap.end()) { diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.h b/cmds/statsd/src/metrics/DurationMetricProducer.h index 3a94d9c775aa..01198a9271d3 100644 --- a/cmds/statsd/src/metrics/DurationMetricProducer.h +++ b/cmds/statsd/src/metrics/DurationMetricProducer.h @@ -40,8 +40,8 @@ class DurationMetricProducer : public MetricProducer { public: DurationMetricProducer( const ConfigKey& key, const DurationMetric& durationMetric, const int conditionIndex, - const vector<ConditionState>& initialConditionCache, const size_t startIndex, - const size_t stopIndex, const size_t stopAllIndex, const bool nesting, + const vector<ConditionState>& initialConditionCache, const int startIndex, + const int stopIndex, const int stopAllIndex, const bool nesting, const sp<ConditionWizard>& wizard, const uint64_t protoHash, const FieldMatcher& internalDimensions, const int64_t timeBaseNs, const int64_t startTimeNs, @@ -112,16 +112,32 @@ private: void flushCurrentBucketLocked(const int64_t& eventTimeNs, const int64_t& nextBucketStartTimeNs) override; + bool onConfigUpdatedLocked( + const StatsdConfig& config, const int configIndex, const int metricIndex, + const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers, + const std::unordered_map<int64_t, int>& oldAtomMatchingTrackerMap, + const std::unordered_map<int64_t, int>& newAtomMatchingTrackerMap, + const sp<EventMatcherWizard>& matcherWizard, + const std::vector<sp<ConditionTracker>>& allConditionTrackers, + const std::unordered_map<int64_t, int>& conditionTrackerMap, + const sp<ConditionWizard>& wizard, + const std::unordered_map<int64_t, int>& metricToActivationMap, + std::unordered_map<int, std::vector<int>>& trackerToMetricMap, + std::unordered_map<int, std::vector<int>>& conditionToMetricMap, + std::unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap, + std::unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap, + std::vector<int>& metricsWithActivation) override; + const DurationMetric_AggregationType mAggregationType; // Index of the SimpleAtomMatcher which defines the start. - const size_t mStartIndex; + int mStartIndex; // Index of the SimpleAtomMatcher which defines the stop. - const size_t mStopIndex; + int mStopIndex; // Index of the SimpleAtomMatcher which defines the stop all for all dimensions. - const size_t mStopAllIndex; + int mStopAllIndex; // nest counting -- for the same key, stops must match the number of starts to make real stop const bool mNested; @@ -167,6 +183,8 @@ private: TestSumDurationWithSplitInFollowingBucket); FRIEND_TEST(DurationMetricProducerTest_PartialBucket, TestMaxDuration); FRIEND_TEST(DurationMetricProducerTest_PartialBucket, TestMaxDurationWithSplitInNextBucket); + + FRIEND_TEST(ConfigUpdateTest, TestUpdateDurationMetrics); }; } // namespace statsd diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h index 18e62d28ba46..92c1a6e62640 100644 --- a/cmds/statsd/src/metrics/MetricProducer.h +++ b/cmds/statsd/src/metrics/MetricProducer.h @@ -569,6 +569,7 @@ protected: FRIEND_TEST(ConfigUpdateTest, TestUpdateCountMetrics); FRIEND_TEST(ConfigUpdateTest, TestUpdateEventMetrics); FRIEND_TEST(ConfigUpdateTest, TestUpdateGaugeMetrics); + FRIEND_TEST(ConfigUpdateTest, TestUpdateDurationMetrics); FRIEND_TEST(ConfigUpdateTest, TestUpdateMetricsMultipleTypes); }; diff --git a/cmds/statsd/src/metrics/duration_helper/DurationTracker.h b/cmds/statsd/src/metrics/duration_helper/DurationTracker.h index 8d59d1362919..657b2e4c3ddf 100644 --- a/cmds/statsd/src/metrics/duration_helper/DurationTracker.h +++ b/cmds/statsd/src/metrics/duration_helper/DurationTracker.h @@ -89,6 +89,12 @@ public: virtual ~DurationTracker(){}; + void onConfigUpdated(const sp<ConditionWizard>& wizard, const int conditionTrackerIndex) { + sp<ConditionWizard> tmpWizard = mWizard; + mWizard = wizard; + mConditionTrackerIndex = conditionTrackerIndex; + }; + virtual void noteStart(const HashableDimensionKey& key, bool condition, const int64_t eventTime, const ConditionKey& conditionKey) = 0; virtual void noteStop(const HashableDimensionKey& key, const int64_t eventTime, @@ -191,7 +197,7 @@ protected: sp<ConditionWizard> mWizard; - const int mConditionTrackerIndex; + int mConditionTrackerIndex; const int64_t mBucketSizeNs; @@ -217,6 +223,8 @@ protected: FRIEND_TEST(OringDurationTrackerTest, TestPredictAnomalyTimestamp); FRIEND_TEST(OringDurationTrackerTest, TestAnomalyDetectionExpiredAlarm); FRIEND_TEST(OringDurationTrackerTest, TestAnomalyDetectionFiredAlarm); + + FRIEND_TEST(ConfigUpdateTest, TestUpdateDurationMetrics); }; } // namespace statsd diff --git a/cmds/statsd/src/metrics/parsing_utils/config_update_utils.cpp b/cmds/statsd/src/metrics/parsing_utils/config_update_utils.cpp index d32f5a947d12..cfc6e3f008d5 100644 --- a/cmds/statsd/src/metrics/parsing_utils/config_update_utils.cpp +++ b/cmds/statsd/src/metrics/parsing_utils/config_update_utils.cpp @@ -697,6 +697,43 @@ bool updateMetrics(const ConfigKey& key, const StatsdConfig& config, const int64 } newMetricProducers.push_back(producer.value()); } + for (int i = 0; i < config.duration_metric_size(); i++, metricIndex++) { + const DurationMetric& metric = config.duration_metric(i); + newMetricProducerMap[metric.id()] = metricIndex; + optional<sp<MetricProducer>> producer; + switch (metricsToUpdate[metricIndex]) { + case UPDATE_PRESERVE: { + producer = updateMetric( + config, i, metricIndex, metric.id(), allAtomMatchingTrackers, + oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, matcherWizard, + allConditionTrackers, conditionTrackerMap, wizard, oldMetricProducerMap, + oldMetricProducers, metricToActivationMap, trackerToMetricMap, + conditionToMetricMap, activationAtomTrackerToMetricMap, + deactivationAtomTrackerToMetricMap, metricsWithActivation); + break; + } + case UPDATE_REPLACE: + case UPDATE_NEW: { + producer = createDurationMetricProducerAndUpdateMetadata( + key, config, timeBaseNs, currentTimeNs, metric, metricIndex, + allAtomMatchingTrackers, newAtomMatchingTrackerMap, allConditionTrackers, + conditionTrackerMap, initialConditionCache, wizard, stateAtomIdMap, + allStateGroupMaps, metricToActivationMap, trackerToMetricMap, + conditionToMetricMap, activationAtomTrackerToMetricMap, + deactivationAtomTrackerToMetricMap, metricsWithActivation); + break; + } + default: { + ALOGE("Metric \"%lld\" update state is unknown. This should never happen", + (long long)metric.id()); + return false; + } + } + if (!producer) { + return false; + } + newMetricProducers.push_back(producer.value()); + } for (int i = 0; i < config.event_metric_size(); i++, metricIndex++) { newMetricProducerMap[config.event_metric(i).id()] = metricIndex; const EventMetric& metric = config.event_metric(i); @@ -770,7 +807,7 @@ bool updateMetrics(const ConfigKey& key, const StatsdConfig& config, const int64 } newMetricProducers.push_back(producer.value()); } - // TODO: perform update for value, duration metric. + // TODO: perform update for value metric. const set<int> atomsAllowedFromAnyUid(config.whitelisted_atom_ids().begin(), config.whitelisted_atom_ids().end()); diff --git a/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.cpp b/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.cpp index 34e265c3b2ea..b7dc2c7fd0de 100644 --- a/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.cpp +++ b/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.cpp @@ -365,7 +365,7 @@ optional<sp<MetricProducer>> createCountMetricProducerAndUpdateMetadata( unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap, vector<int>& metricsWithActivation) { if (!metric.has_id() || !metric.has_what()) { - ALOGW("cannot find metric id or \"what\" in CountMetric \"%lld\"", (long long)metric.id()); + ALOGE("cannot find metric id or \"what\" in CountMetric \"%lld\"", (long long)metric.id()); return nullopt; } int trackerIndex; @@ -423,6 +423,125 @@ optional<sp<MetricProducer>> createCountMetricProducerAndUpdateMetadata( eventDeactivationMap, slicedStateAtoms, stateGroupMap)}; } +optional<sp<MetricProducer>> createDurationMetricProducerAndUpdateMetadata( + const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseNs, + const int64_t currentTimeNs, const DurationMetric& metric, const int metricIndex, + const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers, + const unordered_map<int64_t, int>& atomMatchingTrackerMap, + vector<sp<ConditionTracker>>& allConditionTrackers, + const unordered_map<int64_t, int>& conditionTrackerMap, + const vector<ConditionState>& initialConditionCache, const sp<ConditionWizard>& wizard, + const unordered_map<int64_t, int>& stateAtomIdMap, + const unordered_map<int64_t, unordered_map<int, int64_t>>& allStateGroupMaps, + const unordered_map<int64_t, int>& metricToActivationMap, + unordered_map<int, vector<int>>& trackerToMetricMap, + unordered_map<int, vector<int>>& conditionToMetricMap, + unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap, + unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap, + vector<int>& metricsWithActivation) { + if (!metric.has_id() || !metric.has_what()) { + ALOGE("cannot find metric id or \"what\" in DurationMetric \"%lld\"", + (long long)metric.id()); + return nullopt; + } + const auto& what_it = conditionTrackerMap.find(metric.what()); + if (what_it == conditionTrackerMap.end()) { + ALOGE("DurationMetric's \"what\" is not present in the condition trackers"); + return nullopt; + } + + const Predicate& durationWhat = config.predicate(what_it->second); + if (durationWhat.contents_case() != Predicate::ContentsCase::kSimplePredicate) { + ALOGE("DurationMetric's \"what\" must be a simple condition"); + return nullopt; + } + + const SimplePredicate& simplePredicate = durationWhat.simple_predicate(); + bool nesting = simplePredicate.count_nesting(); + + int startIndex = -1, stopIndex = -1, stopAllIndex = -1; + if (!simplePredicate.has_start() || + !handleMetricWithAtomMatchingTrackers( + simplePredicate.start(), metricIndex, metric.has_dimensions_in_what(), + allAtomMatchingTrackers, atomMatchingTrackerMap, trackerToMetricMap, startIndex)) { + ALOGE("Duration metrics must specify a valid start event matcher"); + return nullopt; + } + + if (simplePredicate.has_stop() && + !handleMetricWithAtomMatchingTrackers( + simplePredicate.stop(), metricIndex, metric.has_dimensions_in_what(), + allAtomMatchingTrackers, atomMatchingTrackerMap, trackerToMetricMap, stopIndex)) { + return nullopt; + } + + if (simplePredicate.has_stop_all() && + !handleMetricWithAtomMatchingTrackers(simplePredicate.stop_all(), metricIndex, + metric.has_dimensions_in_what(), + allAtomMatchingTrackers, atomMatchingTrackerMap, + trackerToMetricMap, stopAllIndex)) { + return nullopt; + } + + FieldMatcher internalDimensions = simplePredicate.dimensions(); + + int conditionIndex = -1; + if (metric.has_condition()) { + if (!handleMetricWithConditions(metric.condition(), metricIndex, conditionTrackerMap, + metric.links(), allConditionTrackers, conditionIndex, + conditionToMetricMap)) { + return nullopt; + } + } else if (metric.links_size() > 0) { + ALOGW("metrics has a MetricConditionLink but doesn't have a condition"); + return nullopt; + } + + std::vector<int> slicedStateAtoms; + unordered_map<int, unordered_map<int, int64_t>> stateGroupMap; + if (metric.slice_by_state_size() > 0) { + if (metric.aggregation_type() == DurationMetric::MAX_SPARSE) { + ALOGE("DurationMetric with aggregation type MAX_SPARSE cannot be sliced by state"); + return nullopt; + } + if (!handleMetricWithStates(config, metric.slice_by_state(), stateAtomIdMap, + allStateGroupMaps, slicedStateAtoms, stateGroupMap)) { + return nullopt; + } + } else if (metric.state_link_size() > 0) { + ALOGW("DurationMetric has a MetricStateLink but doesn't have a sliced state"); + return nullopt; + } + + // Check that all metric state links are a subset of dimensions_in_what fields. + std::vector<Matcher> dimensionsInWhat; + translateFieldMatcher(metric.dimensions_in_what(), &dimensionsInWhat); + for (const auto& stateLink : metric.state_link()) { + if (!handleMetricWithStateLink(stateLink.fields_in_what(), dimensionsInWhat)) { + return nullopt; + } + } + + unordered_map<int, shared_ptr<Activation>> eventActivationMap; + unordered_map<int, vector<shared_ptr<Activation>>> eventDeactivationMap; + if (!handleMetricActivation(config, metric.id(), metricIndex, metricToActivationMap, + atomMatchingTrackerMap, activationAtomTrackerToMetricMap, + deactivationAtomTrackerToMetricMap, metricsWithActivation, + eventActivationMap, eventDeactivationMap)) { + return nullopt; + } + + uint64_t metricHash; + if (!getMetricProtoHash(config, metric, metric.id(), metricToActivationMap, metricHash)) { + return nullopt; + } + + return {new DurationMetricProducer( + key, metric, conditionIndex, initialConditionCache, startIndex, stopIndex, stopAllIndex, + nesting, wizard, metricHash, internalDimensions, timeBaseNs, currentTimeNs, + eventActivationMap, eventDeactivationMap, slicedStateAtoms, stateGroupMap)}; +} + optional<sp<MetricProducer>> createEventMetricProducerAndUpdateMetadata( const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseNs, const EventMetric& metric, const int metricIndex, @@ -438,7 +557,7 @@ optional<sp<MetricProducer>> createEventMetricProducerAndUpdateMetadata( unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap, vector<int>& metricsWithActivation) { if (!metric.has_id() || !metric.has_what()) { - ALOGW("cannot find the metric name or what in config"); + ALOGE("cannot find the metric name or what in config"); return nullopt; } int trackerIndex; @@ -497,7 +616,7 @@ optional<sp<MetricProducer>> createGaugeMetricProducerAndUpdateMetadata( unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap, vector<int>& metricsWithActivation) { if (!metric.has_id() || !metric.has_what()) { - ALOGW("cannot find metric id or \"what\" in GaugeMetric \"%lld\"", (long long)metric.id()); + ALOGE("cannot find metric id or \"what\" in GaugeMetric \"%lld\"", (long long)metric.id()); return nullopt; } @@ -760,114 +879,17 @@ bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const int64_t const DurationMetric& metric = config.duration_metric(i); metricMap.insert({metric.id(), metricIndex}); - auto what_it = conditionTrackerMap.find(metric.what()); - if (what_it == conditionTrackerMap.end()) { - ALOGE("DurationMetric's \"what\" is invalid"); - return false; - } - - const Predicate& durationWhat = config.predicate(what_it->second); - - if (durationWhat.contents_case() != Predicate::ContentsCase::kSimplePredicate) { - ALOGE("DurationMetric's \"what\" must be a simple condition"); - return false; - } - - const auto& simplePredicate = durationWhat.simple_predicate(); - - bool nesting = simplePredicate.count_nesting(); - - int trackerIndices[3] = {-1, -1, -1}; - if (!simplePredicate.has_start() || - !handleMetricWithAtomMatchingTrackers(simplePredicate.start(), metricIndex, - metric.has_dimensions_in_what(), - allAtomMatchingTrackers, atomMatchingTrackerMap, - trackerToMetricMap, trackerIndices[0])) { - ALOGE("Duration metrics must specify a valid the start event matcher"); - return false; - } - - if (simplePredicate.has_stop() && - !handleMetricWithAtomMatchingTrackers(simplePredicate.stop(), metricIndex, - metric.has_dimensions_in_what(), - allAtomMatchingTrackers, atomMatchingTrackerMap, - trackerToMetricMap, trackerIndices[1])) { - return false; - } - - if (simplePredicate.has_stop_all() && - !handleMetricWithAtomMatchingTrackers(simplePredicate.stop_all(), metricIndex, - metric.has_dimensions_in_what(), - allAtomMatchingTrackers, atomMatchingTrackerMap, - trackerToMetricMap, trackerIndices[2])) { - return false; - } - - FieldMatcher internalDimensions = simplePredicate.dimensions(); - - int conditionIndex = -1; - - if (metric.has_condition()) { - bool good = handleMetricWithConditions( - metric.condition(), metricIndex, conditionTrackerMap, metric.links(), - allConditionTrackers, conditionIndex, conditionToMetricMap); - if (!good) { - return false; - } - } else { - if (metric.links_size() > 0) { - ALOGW("metrics has a MetricConditionLink but doesn't have a condition"); - return false; - } - } - - std::vector<int> slicedStateAtoms; - unordered_map<int, unordered_map<int, int64_t>> stateGroupMap; - if (metric.slice_by_state_size() > 0) { - if (metric.aggregation_type() == DurationMetric::MAX_SPARSE) { - ALOGE("DurationMetric with aggregation type MAX_SPARSE cannot be sliced by state"); - return false; - } - if (!handleMetricWithStates(config, metric.slice_by_state(), stateAtomIdMap, - allStateGroupMaps, slicedStateAtoms, stateGroupMap)) { - return false; - } - } else { - if (metric.state_link_size() > 0) { - ALOGW("DurationMetric has a MetricStateLink but doesn't have a sliced state"); - return false; - } - } - - // Check that all metric state links are a subset of dimensions_in_what fields. - std::vector<Matcher> dimensionsInWhat; - translateFieldMatcher(metric.dimensions_in_what(), &dimensionsInWhat); - for (const auto& stateLink : metric.state_link()) { - if (!handleMetricWithStateLink(stateLink.fields_in_what(), dimensionsInWhat)) { - return false; - } - } - - unordered_map<int, shared_ptr<Activation>> eventActivationMap; - unordered_map<int, vector<shared_ptr<Activation>>> eventDeactivationMap; - bool success = handleMetricActivation( - config, metric.id(), metricIndex, metricToActivationMap, atomMatchingTrackerMap, + optional<sp<MetricProducer>> producer = createDurationMetricProducerAndUpdateMetadata( + key, config, timeBaseTimeNs, currentTimeNs, metric, metricIndex, + allAtomMatchingTrackers, atomMatchingTrackerMap, allConditionTrackers, + conditionTrackerMap, initialConditionCache, wizard, stateAtomIdMap, + allStateGroupMaps, metricToActivationMap, trackerToMetricMap, conditionToMetricMap, activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, - metricsWithActivation, eventActivationMap, eventDeactivationMap); - if (!success) return false; - - uint64_t metricHash; - if (!getMetricProtoHash(config, metric, metric.id(), metricToActivationMap, metricHash)) { + metricsWithActivation); + if (!producer) { return false; } - - sp<MetricProducer> durationMetric = new DurationMetricProducer( - key, metric, conditionIndex, initialConditionCache, trackerIndices[0], - trackerIndices[1], trackerIndices[2], nesting, wizard, metricHash, - internalDimensions, timeBaseTimeNs, currentTimeNs, eventActivationMap, - eventDeactivationMap, slicedStateAtoms, stateGroupMap); - - allMetricProducers.push_back(durationMetric); + allMetricProducers.push_back(producer.value()); } // build EventMetricProducer diff --git a/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.h b/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.h index f909aff48faf..6d1e6dde7e89 100644 --- a/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.h +++ b/cmds/statsd/src/metrics/parsing_utils/metrics_manager_util.h @@ -112,6 +112,25 @@ optional<sp<MetricProducer>> createCountMetricProducerAndUpdateMetadata( std::unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap, std::vector<int>& metricsWithActivation); +// Creates a DurationMetricProducer and updates the vectors/maps used by MetricsManager with +// the appropriate indices. Returns an sp to the producer, or nullopt if there was an error. +optional<sp<MetricProducer>> createDurationMetricProducerAndUpdateMetadata( + const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseNs, + const int64_t currentTimeNs, const DurationMetric& metric, const int metricIndex, + const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers, + const std::unordered_map<int64_t, int>& atomMatchingTrackerMap, + std::vector<sp<ConditionTracker>>& allConditionTrackers, + const std::unordered_map<int64_t, int>& conditionTrackerMap, + const std::vector<ConditionState>& initialConditionCache, const sp<ConditionWizard>& wizard, + const std::unordered_map<int64_t, int>& stateAtomIdMap, + const std::unordered_map<int64_t, std::unordered_map<int, int64_t>>& allStateGroupMaps, + const std::unordered_map<int64_t, int>& metricToActivationMap, + std::unordered_map<int, std::vector<int>>& trackerToMetricMap, + std::unordered_map<int, std::vector<int>>& conditionToMetricMap, + std::unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap, + std::unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap, + std::vector<int>& metricsWithActivation); + // Creates an EventMetricProducer and updates the vectors/maps used by MetricsManager with // the appropriate indices. Returns an sp to the producer, or nullopt if there was an error. optional<sp<MetricProducer>> createEventMetricProducerAndUpdateMetadata( diff --git a/cmds/statsd/tests/metrics/parsing_utils/config_update_utils_test.cpp b/cmds/statsd/tests/metrics/parsing_utils/config_update_utils_test.cpp index a20be15d9541..dc951be72fe1 100644 --- a/cmds/statsd/tests/metrics/parsing_utils/config_update_utils_test.cpp +++ b/cmds/statsd/tests/metrics/parsing_utils/config_update_utils_test.cpp @@ -27,6 +27,7 @@ #include "src/condition/CombinationConditionTracker.h" #include "src/condition/SimpleConditionTracker.h" #include "src/matchers/CombinationAtomMatchingTracker.h" +#include "src/metrics/DurationMetricProducer.h" #include "src/metrics/GaugeMetricProducer.h" #include "src/metrics/parsing_utils/metrics_manager_util.h" #include "tests/statsd_test_util.h" @@ -2212,6 +2213,352 @@ TEST_F(ConfigUpdateTest, TestUpdateGaugeMetrics) { EXPECT_EQ(oldMatcherWizard->getStrongCount(), 1); } +TEST_F(ConfigUpdateTest, TestUpdateDurationMetrics) { + StatsdConfig config; + // Add atom matchers/predicates/states. These are mostly needed for initStatsdConfig. + AtomMatcher matcher1 = CreateScreenTurnedOnAtomMatcher(); + int64_t matcher1Id = matcher1.id(); + *config.add_atom_matcher() = matcher1; + + AtomMatcher matcher2 = CreateScreenTurnedOffAtomMatcher(); + int64_t matcher2Id = matcher2.id(); + *config.add_atom_matcher() = matcher2; + + AtomMatcher matcher3 = CreateAcquireWakelockAtomMatcher(); + int64_t matcher3Id = matcher3.id(); + *config.add_atom_matcher() = matcher3; + + AtomMatcher matcher4 = CreateReleaseWakelockAtomMatcher(); + int64_t matcher4Id = matcher4.id(); + *config.add_atom_matcher() = matcher4; + + AtomMatcher matcher5 = CreateMoveToForegroundAtomMatcher(); + int64_t matcher5Id = matcher5.id(); + *config.add_atom_matcher() = matcher5; + + AtomMatcher matcher6 = CreateMoveToBackgroundAtomMatcher(); + int64_t matcher6Id = matcher6.id(); + *config.add_atom_matcher() = matcher6; + + AtomMatcher matcher7 = CreateBatteryStateNoneMatcher(); + int64_t matcher7Id = matcher7.id(); + *config.add_atom_matcher() = matcher7; + + AtomMatcher matcher8 = CreateBatteryStateUsbMatcher(); + int64_t matcher8Id = matcher8.id(); + *config.add_atom_matcher() = matcher8; + + Predicate predicate1 = CreateScreenIsOnPredicate(); + int64_t predicate1Id = predicate1.id(); + *config.add_predicate() = predicate1; + + Predicate predicate2 = CreateScreenIsOffPredicate(); + int64_t predicate2Id = predicate2.id(); + *config.add_predicate() = predicate2; + + Predicate predicate3 = CreateDeviceUnpluggedPredicate(); + int64_t predicate3Id = predicate3.id(); + *config.add_predicate() = predicate3; + + Predicate predicate4 = CreateIsInBackgroundPredicate(); + *predicate4.mutable_simple_predicate()->mutable_dimensions() = + CreateDimensions(util::ACTIVITY_FOREGROUND_STATE_CHANGED, {1}); + int64_t predicate4Id = predicate4.id(); + *config.add_predicate() = predicate4; + + Predicate predicate5 = CreateHoldingWakelockPredicate(); + *predicate5.mutable_simple_predicate()->mutable_dimensions() = + CreateAttributionUidDimensions(util::WAKELOCK_STATE_CHANGED, {Position::FIRST}); + predicate5.mutable_simple_predicate()->set_stop_all(matcher7Id); + int64_t predicate5Id = predicate5.id(); + *config.add_predicate() = predicate5; + + State state1 = CreateScreenStateWithOnOffMap(0x123, 0x321); + int64_t state1Id = state1.id(); + *config.add_state() = state1; + + State state2 = CreateScreenState(); + int64_t state2Id = state2.id(); + *config.add_state() = state2; + + // Add a few duration metrics. + // Will be preserved. + DurationMetric duration1 = + createDurationMetric("DURATION1", predicate5Id, predicate4Id, {state2Id}); + *duration1.mutable_dimensions_in_what() = + CreateAttributionUidDimensions(util::WAKELOCK_STATE_CHANGED, {Position::FIRST}); + MetricConditionLink* link = duration1.add_links(); + link->set_condition(predicate4Id); + *link->mutable_fields_in_what() = + CreateAttributionUidDimensions(util::WAKELOCK_STATE_CHANGED, {Position::FIRST}); + *link->mutable_fields_in_condition() = + CreateDimensions(util::ACTIVITY_FOREGROUND_STATE_CHANGED, {1} /*uid field*/); + int64_t duration1Id = duration1.id(); + *config.add_duration_metric() = duration1; + + // Will be replaced. + DurationMetric duration2 = createDurationMetric("DURATION2", predicate1Id, nullopt, {}); + int64_t duration2Id = duration2.id(); + *config.add_duration_metric() = duration2; + + // Will be replaced. + DurationMetric duration3 = createDurationMetric("DURATION3", predicate3Id, nullopt, {state1Id}); + int64_t duration3Id = duration3.id(); + *config.add_duration_metric() = duration3; + + // Will be replaced. + DurationMetric duration4 = createDurationMetric("DURATION4", predicate3Id, predicate2Id, {}); + int64_t duration4Id = duration4.id(); + *config.add_duration_metric() = duration4; + + // Will be deleted. + DurationMetric duration5 = createDurationMetric("DURATION5", predicate2Id, nullopt, {}); + int64_t duration5Id = duration5.id(); + *config.add_duration_metric() = duration5; + + EXPECT_TRUE(initConfig(config)); + + // Make some sliced conditions true. + int uid1 = 10; + int uid2 = 11; + vector<MatchingState> matchingStates(8, MatchingState::kNotMatched); + matchingStates[2] = kMatched; + vector<ConditionState> conditionCache(5, ConditionState::kNotEvaluated); + vector<bool> changedCache(5, false); + unique_ptr<LogEvent> event = CreateAcquireWakelockEvent(timeBaseNs + 3, {uid1}, {"tag"}, "wl1"); + oldConditionTrackers[4]->evaluateCondition(*event.get(), matchingStates, oldConditionTrackers, + conditionCache, changedCache); + EXPECT_TRUE(oldConditionTrackers[4]->isSliced()); + EXPECT_TRUE(changedCache[4]); + EXPECT_EQ(conditionCache[4], ConditionState::kTrue); + oldMetricProducers[0]->onMatchedLogEvent(2, *event.get()); + + fill(conditionCache.begin(), conditionCache.end(), ConditionState::kNotEvaluated); + fill(changedCache.begin(), changedCache.end(), false); + event = CreateAcquireWakelockEvent(timeBaseNs + 3, {uid2}, {"tag"}, "wl2"); + oldConditionTrackers[4]->evaluateCondition(*event.get(), matchingStates, oldConditionTrackers, + conditionCache, changedCache); + EXPECT_TRUE(changedCache[4]); + EXPECT_EQ(conditionCache[4], ConditionState::kTrue); + oldMetricProducers[0]->onMatchedLogEvent(2, *event.get()); + + // Used later to ensure the condition wizard is replaced. Get it before doing the update. + // The duration trackers have a pointer to the wizard, and 2 trackers were created above. + sp<ConditionWizard> oldConditionWizard = oldMetricProducers[0]->mWizard; + EXPECT_EQ(oldConditionWizard->getStrongCount(), 8); + + // Replace predicate1, predicate3, and state1. Causes duration2/3/4 to be replaced. + set<int64_t> replacedConditions({predicate1Id, predicate2Id}); + set<int64_t> replacedStates({state1Id}); + + // New duration metric. + DurationMetric duration6 = createDurationMetric("DURATION6", predicate4Id, predicate5Id, {}); + *duration6.mutable_dimensions_in_what() = + CreateDimensions(util::ACTIVITY_FOREGROUND_STATE_CHANGED, {1} /*uid field*/); + link = duration6.add_links(); + link->set_condition(predicate5Id); + *link->mutable_fields_in_what() = + CreateDimensions(util::ACTIVITY_FOREGROUND_STATE_CHANGED, {1} /*uid field*/); + *link->mutable_fields_in_condition() = + CreateAttributionUidDimensions(util::WAKELOCK_STATE_CHANGED, {Position::FIRST}); + int64_t duration6Id = duration6.id(); + + // Map the matchers and predicates in reverse order to force the indices to change. + const int matcher8Index = 0, matcher7Index = 1, matcher6Index = 2, matcher5Index = 3, + matcher4Index = 4, matcher3Index = 5, matcher2Index = 6, matcher1Index = 7; + std::unordered_map<int64_t, int> newAtomMatchingTrackerMap({{matcher8Id, matcher8Index}, + {matcher7Id, matcher7Index}, + {matcher6Id, matcher6Index}, + {matcher5Id, matcher5Index}, + {matcher4Id, matcher4Index}, + {matcher3Id, matcher3Index}, + {matcher2Id, matcher2Index}, + {matcher1Id, matcher1Index}}); + // Use the existing matchers. A bit hacky, but saves code and we don't rely on them. + vector<sp<AtomMatchingTracker>> newAtomMatchingTrackers(8); + reverse_copy(oldAtomMatchingTrackers.begin(), oldAtomMatchingTrackers.end(), + newAtomMatchingTrackers.begin()); + + const int predicate5Index = 0, predicate4Index = 1, predicate3Index = 2, predicate2Index = 3, + predicate1Index = 4; + std::unordered_map<int64_t, int> newConditionTrackerMap({ + {predicate5Id, predicate5Index}, + {predicate4Id, predicate4Index}, + {predicate3Id, predicate3Index}, + {predicate2Id, predicate2Index}, + {predicate1Id, predicate1Index}, + }); + // Use the existing conditionTrackers and reinitialize them to get the initial condition cache. + vector<sp<ConditionTracker>> newConditionTrackers(5); + reverse_copy(oldConditionTrackers.begin(), oldConditionTrackers.end(), + newConditionTrackers.begin()); + vector<Predicate> conditionProtos(5); + reverse_copy(config.predicate().begin(), config.predicate().end(), conditionProtos.begin()); + for (int i = 0; i < newConditionTrackers.size(); i++) { + EXPECT_TRUE(newConditionTrackers[i]->onConfigUpdated( + conditionProtos, i, newConditionTrackers, newAtomMatchingTrackerMap, + newConditionTrackerMap)); + } + vector<bool> cycleTracker(5, false); + fill(conditionCache.begin(), conditionCache.end(), ConditionState::kNotEvaluated); + for (int i = 0; i < newConditionTrackers.size(); i++) { + EXPECT_TRUE(newConditionTrackers[i]->init(conditionProtos, newConditionTrackers, + newConditionTrackerMap, cycleTracker, + conditionCache)); + } + // Predicate5 should be true since 2 uids have wakelocks + EXPECT_EQ(conditionCache, vector({kTrue, kUnknown, kUnknown, kUnknown, kUnknown})); + + StatsdConfig newConfig; + *newConfig.add_duration_metric() = duration6; + const int duration6Index = 0; + *newConfig.add_duration_metric() = duration3; + const int duration3Index = 1; + *newConfig.add_duration_metric() = duration1; + const int duration1Index = 2; + *newConfig.add_duration_metric() = duration4; + const int duration4Index = 3; + *newConfig.add_duration_metric() = duration2; + const int duration2Index = 4; + + for (const Predicate& predicate : conditionProtos) { + *newConfig.add_predicate() = predicate; + } + *newConfig.add_state() = state1; + *newConfig.add_state() = state2; + unordered_map<int64_t, int> stateAtomIdMap; + unordered_map<int64_t, unordered_map<int, int64_t>> allStateGroupMaps; + map<int64_t, uint64_t> stateProtoHashes; + EXPECT_TRUE(initStates(newConfig, stateAtomIdMap, allStateGroupMaps, stateProtoHashes)); + + // Output data structures to validate. + unordered_map<int64_t, int> newMetricProducerMap; + vector<sp<MetricProducer>> newMetricProducers; + unordered_map<int, vector<int>> conditionToMetricMap; + unordered_map<int, vector<int>> trackerToMetricMap; + set<int64_t> noReportMetricIds; + unordered_map<int, vector<int>> activationAtomTrackerToMetricMap; + unordered_map<int, vector<int>> deactivationAtomTrackerToMetricMap; + vector<int> metricsWithActivation; + EXPECT_TRUE(updateMetrics( + key, newConfig, /*timeBaseNs=*/123, /*currentTimeNs=*/12345, new StatsPullerManager(), + oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, /*replacedMatchers=*/{}, + newAtomMatchingTrackers, newConditionTrackerMap, replacedConditions, + newConditionTrackers, conditionCache, stateAtomIdMap, allStateGroupMaps, replacedStates, + oldMetricProducerMap, oldMetricProducers, newMetricProducerMap, newMetricProducers, + conditionToMetricMap, trackerToMetricMap, noReportMetricIds, + activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, + metricsWithActivation)); + + unordered_map<int64_t, int> expectedMetricProducerMap = { + {duration1Id, duration1Index}, {duration2Id, duration2Index}, + {duration3Id, duration3Index}, {duration4Id, duration4Index}, + {duration6Id, duration6Index}, + }; + EXPECT_THAT(newMetricProducerMap, ContainerEq(expectedMetricProducerMap)); + + // Make sure preserved metrics are the same. + ASSERT_EQ(newMetricProducers.size(), 5); + EXPECT_EQ(oldMetricProducers[oldMetricProducerMap.at(duration1Id)], + newMetricProducers[newMetricProducerMap.at(duration1Id)]); + + // Make sure replaced metrics are different. + EXPECT_NE(oldMetricProducers[oldMetricProducerMap.at(duration2Id)], + newMetricProducers[newMetricProducerMap.at(duration2Id)]); + EXPECT_NE(oldMetricProducers[oldMetricProducerMap.at(duration3Id)], + newMetricProducers[newMetricProducerMap.at(duration3Id)]); + EXPECT_NE(oldMetricProducers[oldMetricProducerMap.at(duration4Id)], + newMetricProducers[newMetricProducerMap.at(duration4Id)]); + + // Verify the conditionToMetricMap. Note that the "what" is not in this map. + ASSERT_EQ(conditionToMetricMap.size(), 3); + const vector<int>& condition2Metrics = conditionToMetricMap[predicate2Index]; + EXPECT_THAT(condition2Metrics, UnorderedElementsAre(duration4Index)); + const vector<int>& condition4Metrics = conditionToMetricMap[predicate4Index]; + EXPECT_THAT(condition4Metrics, UnorderedElementsAre(duration1Index)); + const vector<int>& condition5Metrics = conditionToMetricMap[predicate5Index]; + EXPECT_THAT(condition5Metrics, UnorderedElementsAre(duration6Index)); + + // Verify the trackerToMetricMap. The start/stop/stopall indices from the "what" should be here. + ASSERT_EQ(trackerToMetricMap.size(), 8); + const vector<int>& matcher1Metrics = trackerToMetricMap[matcher1Index]; + EXPECT_THAT(matcher1Metrics, UnorderedElementsAre(duration2Index)); + const vector<int>& matcher2Metrics = trackerToMetricMap[matcher2Index]; + EXPECT_THAT(matcher2Metrics, UnorderedElementsAre(duration2Index)); + const vector<int>& matcher3Metrics = trackerToMetricMap[matcher3Index]; + EXPECT_THAT(matcher3Metrics, UnorderedElementsAre(duration1Index)); + const vector<int>& matcher4Metrics = trackerToMetricMap[matcher4Index]; + EXPECT_THAT(matcher4Metrics, UnorderedElementsAre(duration1Index)); + const vector<int>& matcher5Metrics = trackerToMetricMap[matcher5Index]; + EXPECT_THAT(matcher5Metrics, UnorderedElementsAre(duration6Index)); + const vector<int>& matcher6Metrics = trackerToMetricMap[matcher6Index]; + EXPECT_THAT(matcher6Metrics, UnorderedElementsAre(duration6Index)); + const vector<int>& matcher7Metrics = trackerToMetricMap[matcher7Index]; + EXPECT_THAT(matcher7Metrics, + UnorderedElementsAre(duration1Index, duration3Index, duration4Index)); + const vector<int>& matcher8Metrics = trackerToMetricMap[matcher8Index]; + EXPECT_THAT(matcher8Metrics, UnorderedElementsAre(duration3Index, duration4Index)); + + // Verify event activation/deactivation maps. + ASSERT_EQ(activationAtomTrackerToMetricMap.size(), 0); + ASSERT_EQ(deactivationAtomTrackerToMetricMap.size(), 0); + ASSERT_EQ(metricsWithActivation.size(), 0); + + // Verify tracker indices/ids/conditions are correct. + DurationMetricProducer* durationProducer1 = + static_cast<DurationMetricProducer*>(newMetricProducers[duration1Index].get()); + EXPECT_EQ(durationProducer1->getMetricId(), duration1Id); + EXPECT_EQ(durationProducer1->mConditionTrackerIndex, predicate4Index); + EXPECT_EQ(durationProducer1->mCondition, ConditionState::kUnknown); + EXPECT_EQ(durationProducer1->mStartIndex, matcher3Index); + EXPECT_EQ(durationProducer1->mStopIndex, matcher4Index); + EXPECT_EQ(durationProducer1->mStopAllIndex, matcher7Index); + EXPECT_EQ(durationProducer1->mCurrentSlicedDurationTrackerMap.size(), 2); + for (const auto& durationTrackerIt : durationProducer1->mCurrentSlicedDurationTrackerMap) { + EXPECT_EQ(durationTrackerIt.second->mConditionTrackerIndex, predicate4Index); + } + DurationMetricProducer* durationProducer2 = + static_cast<DurationMetricProducer*>(newMetricProducers[duration2Index].get()); + EXPECT_EQ(durationProducer2->getMetricId(), duration2Id); + EXPECT_EQ(durationProducer2->mConditionTrackerIndex, -1); + EXPECT_EQ(durationProducer2->mCondition, ConditionState::kTrue); + EXPECT_EQ(durationProducer2->mStartIndex, matcher1Index); + EXPECT_EQ(durationProducer2->mStopIndex, matcher2Index); + EXPECT_EQ(durationProducer2->mStopAllIndex, -1); + DurationMetricProducer* durationProducer3 = + static_cast<DurationMetricProducer*>(newMetricProducers[duration3Index].get()); + EXPECT_EQ(durationProducer3->getMetricId(), duration3Id); + EXPECT_EQ(durationProducer3->mConditionTrackerIndex, -1); + EXPECT_EQ(durationProducer3->mCondition, ConditionState::kTrue); + EXPECT_EQ(durationProducer3->mStartIndex, matcher7Index); + EXPECT_EQ(durationProducer3->mStopIndex, matcher8Index); + EXPECT_EQ(durationProducer3->mStopAllIndex, -1); + DurationMetricProducer* durationProducer4 = + static_cast<DurationMetricProducer*>(newMetricProducers[duration4Index].get()); + EXPECT_EQ(durationProducer4->getMetricId(), duration4Id); + EXPECT_EQ(durationProducer4->mConditionTrackerIndex, predicate2Index); + EXPECT_EQ(durationProducer4->mCondition, ConditionState::kUnknown); + EXPECT_EQ(durationProducer4->mStartIndex, matcher7Index); + EXPECT_EQ(durationProducer4->mStopIndex, matcher8Index); + EXPECT_EQ(durationProducer4->mStopAllIndex, -1); + DurationMetricProducer* durationProducer6 = + static_cast<DurationMetricProducer*>(newMetricProducers[duration6Index].get()); + EXPECT_EQ(durationProducer6->getMetricId(), duration6Id); + EXPECT_EQ(durationProducer6->mConditionTrackerIndex, predicate5Index); + // TODO(b/167491517): should this be unknown since the condition is sliced? + EXPECT_EQ(durationProducer6->mCondition, ConditionState::kTrue); + EXPECT_EQ(durationProducer6->mStartIndex, matcher6Index); + EXPECT_EQ(durationProducer6->mStopIndex, matcher5Index); + EXPECT_EQ(durationProducer6->mStopAllIndex, -1); + + sp<ConditionWizard> newConditionWizard = newMetricProducers[0]->mWizard; + EXPECT_NE(newConditionWizard, oldConditionWizard); + EXPECT_EQ(newConditionWizard->getStrongCount(), 8); + oldMetricProducers.clear(); + // Only reference to the old wizard should be the one in the test. + EXPECT_EQ(oldConditionWizard->getStrongCount(), 1); +} + TEST_F(ConfigUpdateTest, TestUpdateMetricActivations) { StatsdConfig config; // Add atom matchers @@ -2376,11 +2723,16 @@ TEST_F(ConfigUpdateTest, TestUpdateMetricsMultipleTypes) { int64_t gaugeMetricId = gaugeMetric.id(); *config.add_gauge_metric() = gaugeMetric; + // Preserved. + DurationMetric durationMetric = createDurationMetric("DURATION1", predicate1Id, nullopt, {}); + int64_t durationMetricId = durationMetric.id(); + *config.add_duration_metric() = durationMetric; + EXPECT_TRUE(initConfig(config)); // Used later to ensure the condition wizard is replaced. Get it before doing the update. sp<ConditionWizard> oldConditionWizard = oldMetricProducers[0]->mWizard; - EXPECT_EQ(oldConditionWizard->getStrongCount(), 4); + EXPECT_EQ(oldConditionWizard->getStrongCount(), 5); // Mark matcher 2 as replaced. Causes eventMetric to be replaced. set<int64_t> replacedMatchers; @@ -2414,10 +2766,15 @@ TEST_F(ConfigUpdateTest, TestUpdateMetricsMultipleTypes) { StatsdConfig newConfig; *newConfig.add_count_metric() = countMetric; const int countMetricIndex = 0; + *newConfig.add_duration_metric() = durationMetric; + const int durationMetricIndex = 1; *newConfig.add_event_metric() = eventMetric; - const int eventMetricIndex = 1; + const int eventMetricIndex = 2; *newConfig.add_gauge_metric() = gaugeMetric; - const int gaugeMetricIndex = 2; + const int gaugeMetricIndex = 3; + + // Add the predicate since duration metric needs it. + *newConfig.add_predicate() = predicate1; // Output data structures to validate. unordered_map<int64_t, int> newMetricProducerMap; @@ -2440,15 +2797,18 @@ TEST_F(ConfigUpdateTest, TestUpdateMetricsMultipleTypes) { unordered_map<int64_t, int> expectedMetricProducerMap = { {countMetricId, countMetricIndex}, + {durationMetricId, durationMetricIndex}, {eventMetricId, eventMetricIndex}, {gaugeMetricId, gaugeMetricIndex}, }; EXPECT_THAT(newMetricProducerMap, ContainerEq(expectedMetricProducerMap)); // Make sure preserved metrics are the same. - ASSERT_EQ(newMetricProducers.size(), 3); + ASSERT_EQ(newMetricProducers.size(), 4); EXPECT_EQ(oldMetricProducers[oldMetricProducerMap.at(countMetricId)], newMetricProducers[newMetricProducerMap.at(countMetricId)]); + EXPECT_EQ(oldMetricProducers[oldMetricProducerMap.at(durationMetricId)], + newMetricProducers[newMetricProducerMap.at(durationMetricId)]); // Make sure replaced metrics are different. EXPECT_NE(oldMetricProducers[oldMetricProducerMap.at(eventMetricId)], @@ -2464,9 +2824,9 @@ TEST_F(ConfigUpdateTest, TestUpdateMetricsMultipleTypes) { // Verify the trackerToMetricMap. ASSERT_EQ(trackerToMetricMap.size(), 3); const vector<int>& matcher1Metrics = trackerToMetricMap[matcher1Index]; - EXPECT_THAT(matcher1Metrics, UnorderedElementsAre(countMetricIndex)); + EXPECT_THAT(matcher1Metrics, UnorderedElementsAre(countMetricIndex, durationMetricIndex)); const vector<int>& matcher2Metrics = trackerToMetricMap[matcher2Index]; - EXPECT_THAT(matcher2Metrics, UnorderedElementsAre(eventMetricIndex)); + EXPECT_THAT(matcher2Metrics, UnorderedElementsAre(eventMetricIndex, durationMetricIndex)); const vector<int>& matcher3Metrics = trackerToMetricMap[matcher3Index]; EXPECT_THAT(matcher3Metrics, UnorderedElementsAre(gaugeMetricIndex)); @@ -2479,6 +2839,9 @@ TEST_F(ConfigUpdateTest, TestUpdateMetricsMultipleTypes) { EXPECT_EQ(newMetricProducers[countMetricIndex]->getMetricId(), countMetricId); EXPECT_EQ(newMetricProducers[countMetricIndex]->mConditionTrackerIndex, predicate1Index); EXPECT_EQ(newMetricProducers[countMetricIndex]->mCondition, ConditionState::kUnknown); + EXPECT_EQ(newMetricProducers[durationMetricIndex]->getMetricId(), durationMetricId); + EXPECT_EQ(newMetricProducers[durationMetricIndex]->mConditionTrackerIndex, -1); + EXPECT_EQ(newMetricProducers[durationMetricIndex]->mCondition, ConditionState::kTrue); EXPECT_EQ(newMetricProducers[eventMetricIndex]->getMetricId(), eventMetricId); EXPECT_EQ(newMetricProducers[eventMetricIndex]->mConditionTrackerIndex, -1); EXPECT_EQ(newMetricProducers[eventMetricIndex]->mCondition, ConditionState::kTrue); @@ -2488,7 +2851,7 @@ TEST_F(ConfigUpdateTest, TestUpdateMetricsMultipleTypes) { sp<ConditionWizard> newConditionWizard = newMetricProducers[0]->mWizard; EXPECT_NE(newConditionWizard, oldConditionWizard); - EXPECT_EQ(newConditionWizard->getStrongCount(), 4); + EXPECT_EQ(newConditionWizard->getStrongCount(), 5); oldMetricProducers.clear(); // Only reference to the old wizard should be the one in the test. EXPECT_EQ(oldConditionWizard->getStrongCount(), 1); diff --git a/config/boot-image-profile.txt b/config/boot-image-profile.txt index f43bd2bd61b9..01272c7966ac 100644 --- a/config/boot-image-profile.txt +++ b/config/boot-image-profile.txt @@ -47131,8 +47131,8 @@ Landroid/view/IRemoteAnimationRunner; Landroid/view/IRotationWatcher$Stub$Proxy; Landroid/view/IRotationWatcher$Stub; Landroid/view/IRotationWatcher; -Landroid/view/IScrollCaptureController$Stub; -Landroid/view/IScrollCaptureController; +Landroid/view/IScrollCaptureCallbacks$Stub; +Landroid/view/IScrollCaptureCallbacks; Landroid/view/ISystemGestureExclusionListener$Stub$Proxy; Landroid/view/ISystemGestureExclusionListener$Stub; Landroid/view/ISystemGestureExclusionListener; diff --git a/core/java/android/accessibilityservice/AccessibilityGestureEvent.java b/core/java/android/accessibilityservice/AccessibilityGestureEvent.java index 25729abf9e05..d4713cb611c3 100644 --- a/core/java/android/accessibilityservice/AccessibilityGestureEvent.java +++ b/core/java/android/accessibilityservice/AccessibilityGestureEvent.java @@ -20,6 +20,7 @@ package android.accessibilityservice; import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_DOUBLE_TAP; import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_DOUBLE_TAP_AND_HOLD; import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SINGLE_TAP; +import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SINGLE_TAP_AND_HOLD; import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SWIPE_DOWN; import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SWIPE_LEFT; import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SWIPE_RIGHT; @@ -28,11 +29,13 @@ import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_DOUBLE_TAP; import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_DOUBLE_TAP_AND_HOLD; import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_SINGLE_TAP; +import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_SINGLE_TAP_AND_HOLD; import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_SWIPE_DOWN; import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_SWIPE_LEFT; import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_SWIPE_RIGHT; import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_SWIPE_UP; import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_TRIPLE_TAP; +import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_TRIPLE_TAP_AND_HOLD; import static android.accessibilityservice.AccessibilityService.GESTURE_4_FINGER_DOUBLE_TAP; import static android.accessibilityservice.AccessibilityService.GESTURE_4_FINGER_DOUBLE_TAP_AND_HOLD; import static android.accessibilityservice.AccessibilityService.GESTURE_4_FINGER_SINGLE_TAP; @@ -43,6 +46,7 @@ import static android.accessibilityservice.AccessibilityService.GESTURE_4_FINGER import static android.accessibilityservice.AccessibilityService.GESTURE_4_FINGER_TRIPLE_TAP; import static android.accessibilityservice.AccessibilityService.GESTURE_DOUBLE_TAP; import static android.accessibilityservice.AccessibilityService.GESTURE_DOUBLE_TAP_AND_HOLD; +import static android.accessibilityservice.AccessibilityService.GESTURE_PASSTHROUGH; import static android.accessibilityservice.AccessibilityService.GESTURE_SWIPE_DOWN; import static android.accessibilityservice.AccessibilityService.GESTURE_SWIPE_DOWN_AND_LEFT; import static android.accessibilityservice.AccessibilityService.GESTURE_SWIPE_DOWN_AND_RIGHT; @@ -59,15 +63,21 @@ import static android.accessibilityservice.AccessibilityService.GESTURE_SWIPE_UP import static android.accessibilityservice.AccessibilityService.GESTURE_SWIPE_UP_AND_DOWN; import static android.accessibilityservice.AccessibilityService.GESTURE_SWIPE_UP_AND_LEFT; import static android.accessibilityservice.AccessibilityService.GESTURE_SWIPE_UP_AND_RIGHT; +import static android.accessibilityservice.AccessibilityService.GESTURE_TOUCH_EXPLORATION; +import static android.accessibilityservice.AccessibilityService.GESTURE_UNKNOWN; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.TestApi; +import android.content.pm.ParceledListSlice; import android.os.Parcel; import android.os.Parcelable; +import android.view.MotionEvent; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.ArrayList; +import java.util.List; /** * This class describes the gesture event including gesture id and which display it happens @@ -84,14 +94,19 @@ public final class AccessibilityGestureEvent implements Parcelable { /** @hide */ @IntDef(prefix = { "GESTURE_" }, value = { + GESTURE_UNKNOWN, + GESTURE_TOUCH_EXPLORATION, GESTURE_2_FINGER_SINGLE_TAP, + GESTURE_2_FINGER_SINGLE_TAP_AND_HOLD, GESTURE_2_FINGER_DOUBLE_TAP, GESTURE_2_FINGER_DOUBLE_TAP_AND_HOLD, GESTURE_2_FINGER_TRIPLE_TAP, GESTURE_3_FINGER_SINGLE_TAP, + GESTURE_3_FINGER_SINGLE_TAP_AND_HOLD, GESTURE_3_FINGER_DOUBLE_TAP, GESTURE_3_FINGER_DOUBLE_TAP_AND_HOLD, GESTURE_3_FINGER_TRIPLE_TAP, + GESTURE_3_FINGER_TRIPLE_TAP_AND_HOLD, GESTURE_DOUBLE_TAP, GESTURE_DOUBLE_TAP_AND_HOLD, GESTURE_SWIPE_UP, @@ -133,17 +148,27 @@ public final class AccessibilityGestureEvent implements Parcelable { @GestureId private final int mGestureId; private final int mDisplayId; + private List<MotionEvent> mMotionEvents = new ArrayList<>(); /** @hide */ - @TestApi - public AccessibilityGestureEvent(int gestureId, int displayId) { + public AccessibilityGestureEvent( + int gestureId, int displayId, @NonNull List<MotionEvent> motionEvents) { mGestureId = gestureId; mDisplayId = displayId; + mMotionEvents.addAll(motionEvents); + } + + /** @hide */ + @TestApi + public AccessibilityGestureEvent(int gestureId, int displayId) { + this(gestureId, displayId, new ArrayList<MotionEvent>()); } private AccessibilityGestureEvent(@NonNull Parcel parcel) { mGestureId = parcel.readInt(); mDisplayId = parcel.readInt(); + ParceledListSlice<MotionEvent> slice = parcel.readParcelable(getClass().getClassLoader()); + mMotionEvents = slice.getList(); } /** @@ -166,6 +191,15 @@ public final class AccessibilityGestureEvent implements Parcelable { return mGestureId; } + /** + * Returns the motion events that lead to this gesture. + * + */ + @NonNull + public List<MotionEvent> getMotionEvents() { + return mMotionEvents; + } + @NonNull @Override public String toString() { @@ -173,22 +207,42 @@ public final class AccessibilityGestureEvent implements Parcelable { stringBuilder.append("gestureId: ").append(eventTypeToString(mGestureId)); stringBuilder.append(", "); stringBuilder.append("displayId: ").append(mDisplayId); + stringBuilder.append(", "); + stringBuilder.append("Motion Events: ["); + for (int i = 0; i < mMotionEvents.size(); ++i) { + String action = MotionEvent.actionToString(mMotionEvents.get(i).getActionMasked()); + stringBuilder.append(action); + if (i < (mMotionEvents.size() - 1)) { + stringBuilder.append(", "); + } else { + stringBuilder.append("]"); + } + } stringBuilder.append(']'); return stringBuilder.toString(); } private static String eventTypeToString(int eventType) { switch (eventType) { + case GESTURE_UNKNOWN: return "GESTURE_UNKNOWN"; + case GESTURE_PASSTHROUGH: return "GESTURE_PASSTHROUGH"; + case GESTURE_TOUCH_EXPLORATION: return "GESTURE_TOUCH_EXPLORATION"; case GESTURE_2_FINGER_SINGLE_TAP: return "GESTURE_2_FINGER_SINGLE_TAP"; + case GESTURE_2_FINGER_SINGLE_TAP_AND_HOLD: + return "GESTURE_2_FINGER_SINGLE_TAP_AND_HOLD"; case GESTURE_2_FINGER_DOUBLE_TAP: return "GESTURE_2_FINGER_DOUBLE_TAP"; case GESTURE_2_FINGER_DOUBLE_TAP_AND_HOLD: return "GESTURE_2_FINGER_DOUBLE_TAP_AND_HOLD"; case GESTURE_2_FINGER_TRIPLE_TAP: return "GESTURE_2_FINGER_TRIPLE_TAP"; case GESTURE_3_FINGER_SINGLE_TAP: return "GESTURE_3_FINGER_SINGLE_TAP"; + case GESTURE_3_FINGER_SINGLE_TAP_AND_HOLD: + return "GESTURE_3_FINGER_SINGLE_TAP_AND_HOLD"; case GESTURE_3_FINGER_DOUBLE_TAP: return "GESTURE_3_FINGER_DOUBLE_TAP"; case GESTURE_3_FINGER_DOUBLE_TAP_AND_HOLD: return "GESTURE_3_FINGER_DOUBLE_TAP_AND_HOLD"; case GESTURE_3_FINGER_TRIPLE_TAP: return "GESTURE_3_FINGER_TRIPLE_TAP"; + case GESTURE_3_FINGER_TRIPLE_TAP_AND_HOLD: + return "GESTURE_3_FINGER_TRIPLE_TAP_AND_HOLD"; case GESTURE_4_FINGER_SINGLE_TAP: return "GESTURE_4_FINGER_SINGLE_TAP"; case GESTURE_4_FINGER_DOUBLE_TAP: return "GESTURE_4_FINGER_DOUBLE_TAP"; case GESTURE_4_FINGER_DOUBLE_TAP_AND_HOLD: @@ -240,6 +294,7 @@ public final class AccessibilityGestureEvent implements Parcelable { public void writeToParcel(@NonNull Parcel parcel, int flags) { parcel.writeInt(mGestureId); parcel.writeInt(mDisplayId); + parcel.writeParcelable(new ParceledListSlice<MotionEvent>(mMotionEvents), 0); } /** diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java index b5b0ce3a65f3..0ad9e446dfc7 100644 --- a/core/java/android/accessibilityservice/AccessibilityService.java +++ b/core/java/android/accessibilityservice/AccessibilityService.java @@ -235,6 +235,29 @@ import java.util.function.Consumer; public abstract class AccessibilityService extends Service { /** + * The user has performed a touch-exploration gesture on the touch screen without ever + * triggering gesture detection. This gesture is only dispatched when {@link + * AccessibilityServiceInfo#FLAG_SEND_MOTION_EVENTS} is set. + * + * @hide + */ + public static final int GESTURE_TOUCH_EXPLORATION = -2; + + /** + * The user has performed a passthrough gesture on the touch screen without ever triggering + * gesture detection. This gesture is only dispatched when {@link + * AccessibilityServiceInfo#FLAG_SEND_MOTION_EVENTS} is set. + * @hide + */ + public static final int GESTURE_PASSTHROUGH = -1; + + /** + * The user has performed an unrecognized gesture on the touch screen. This gesture is only + * dispatched when {@link AccessibilityServiceInfo#FLAG_SEND_MOTION_EVENTS} is set. + */ + public static final int GESTURE_UNKNOWN = 0; + + /** * The user has performed a swipe up gesture on the touch screen. */ public static final int GESTURE_SWIPE_UP = 1; @@ -421,6 +444,15 @@ public abstract class AccessibilityService extends Service { /** The user has performed a three-finger double tap and hold gesture on the touch screen. */ public static final int GESTURE_3_FINGER_DOUBLE_TAP_AND_HOLD = 41; + /** The user has performed a two-finger single-tap and hold gesture on the touch screen. */ + public static final int GESTURE_2_FINGER_SINGLE_TAP_AND_HOLD = 43; + + /** The user has performed a three-finger single-tap and hold gesture on the touch screen. */ + public static final int GESTURE_3_FINGER_SINGLE_TAP_AND_HOLD = 44; + + /** The user has performed a three-finger triple-tap and hold gesture on the touch screen. */ + public static final int GESTURE_3_FINGER_TRIPLE_TAP_AND_HOLD = 45; + /** The user has performed a two-finger double tap and hold gesture on the touch screen. */ public static final int GESTURE_4_FINGER_DOUBLE_TAP_AND_HOLD = 42; diff --git a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java index d334de60713a..f953da48c217 100644 --- a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java +++ b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java @@ -376,6 +376,20 @@ public class AccessibilityServiceInfo implements Parcelable { */ public static final int FLAG_REQUEST_2_FINGER_PASSTHROUGH = 0x0002000; + /** + * This flag requests that when when {@link #FLAG_REQUEST_TOUCH_EXPLORATION_MODE} is enabled, a + * service will receive the motion events for each successfully-detected gesture. The service + * will also receive an AccessibilityGestureEvent of type GESTURE_INVALID for each cancelled + * gesture along with its motion events. A service will receive a gesture of type + * GESTURE_PASSTHROUGH and accompanying motion events for every passthrough gesture that does + * not start gesture detection. This information can be used to collect instances of improper + * gesture detection behavior and relay that information to framework developers. If {@link + * #FLAG_REQUEST_TOUCH_EXPLORATION_MODE} is disabled this flag has no effect. + * + * @see #FLAG_REQUEST_TOUCH_EXPLORATION_MODE + */ + public static final int FLAG_SEND_MOTION_EVENTS = 0x0004000; + /** {@hide} */ public static final int FLAG_FORCE_DIRECT_BOOT_AWARE = 0x00010000; @@ -1077,7 +1091,7 @@ public class AccessibilityServiceInfo implements Parcelable { } @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { if (this == obj) { return true; } @@ -1276,6 +1290,8 @@ public class AccessibilityServiceInfo implements Parcelable { return "FLAG_REQUEST_MULTI_FINGER_GESTURES"; case FLAG_REQUEST_2_FINGER_PASSTHROUGH: return "FLAG_REQUEST_2_FINGER_PASSTHROUGH"; + case FLAG_SEND_MOTION_EVENTS: + return "FLAG_SEND_MOTION_EVENTS"; case FLAG_REQUEST_ENHANCED_WEB_ACCESSIBILITY: return "FLAG_REQUEST_ENHANCED_WEB_ACCESSIBILITY"; case FLAG_REPORT_VIEW_IDS: diff --git a/core/java/android/accessibilityservice/AccessibilityShortcutInfo.java b/core/java/android/accessibilityservice/AccessibilityShortcutInfo.java index 623734ee5662..fe1cca53c78b 100644 --- a/core/java/android/accessibilityservice/AccessibilityShortcutInfo.java +++ b/core/java/android/accessibilityservice/AccessibilityShortcutInfo.java @@ -287,7 +287,7 @@ public final class AccessibilityShortcutInfo { * {@inheritDoc} */ @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { if (this == obj) { return true; } diff --git a/core/java/android/accounts/Account.java b/core/java/android/accounts/Account.java index 9a18880a353b..b960a7f835d8 100644 --- a/core/java/android/accounts/Account.java +++ b/core/java/android/accounts/Account.java @@ -50,7 +50,7 @@ public class Account implements Parcelable { @UnsupportedAppUsage private final @Nullable String accessId; - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (o == this) return true; if (!(o instanceof Account)) return false; final Account other = (Account)o; diff --git a/core/java/android/accounts/AccountAndUser.java b/core/java/android/accounts/AccountAndUser.java index fd6739410ce2..adbc4e9ee001 100644 --- a/core/java/android/accounts/AccountAndUser.java +++ b/core/java/android/accounts/AccountAndUser.java @@ -16,6 +16,7 @@ package android.accounts; +import android.annotation.Nullable; import android.compat.annotation.UnsupportedAppUsage; /** @@ -35,7 +36,7 @@ public class AccountAndUser { this.userId = userId; } - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (this == o) return true; if (!(o instanceof AccountAndUser)) return false; final AccountAndUser other = (AccountAndUser) o; diff --git a/core/java/android/accounts/AccountManager.java b/core/java/android/accounts/AccountManager.java index 9a810a71139d..9bb02cdcce73 100644 --- a/core/java/android/accounts/AccountManager.java +++ b/core/java/android/accounts/AccountManager.java @@ -19,6 +19,7 @@ package android.accounts; import android.annotation.BroadcastBehavior; import android.annotation.IntDef; import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; @@ -357,7 +358,7 @@ public class AccountManager { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (o == null) { return false; } @@ -419,7 +420,7 @@ public class AccountManager { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (o == null) { return false; } diff --git a/core/java/android/accounts/AuthenticatorDescription.java b/core/java/android/accounts/AuthenticatorDescription.java index b7bf11d4d5b6..4be353850815 100644 --- a/core/java/android/accounts/AuthenticatorDescription.java +++ b/core/java/android/accounts/AuthenticatorDescription.java @@ -16,6 +16,7 @@ package android.accounts; +import android.annotation.Nullable; import android.compat.annotation.UnsupportedAppUsage; import android.os.Build; import android.os.Parcel; @@ -111,7 +112,7 @@ public class AuthenticatorDescription implements Parcelable { } /** Compares the type only, suitable for key comparisons. */ - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (o == this) return true; if (!(o instanceof AuthenticatorDescription)) return false; final AuthenticatorDescription other = (AuthenticatorDescription) o; diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index e75d2f60bb87..250f2f0b2dc9 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -100,6 +100,7 @@ import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Locale; +import java.util.concurrent.Executor; /** * <p> @@ -1619,7 +1620,7 @@ public class ActivityManager { } @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { if (!(obj instanceof TaskDescription)) { return false; } @@ -3607,7 +3608,7 @@ public class ActivityManager { * running its code, {@link RunningAppProcessInfo#IMPORTANCE_GONE} is returned. * @hide */ - @SystemApi @TestApi + @SystemApi @RequiresPermission(Manifest.permission.PACKAGE_USAGE_STATS) public @RunningAppProcessInfo.Importance int getPackageImportance(String packageName) { try { @@ -3627,7 +3628,7 @@ public class ActivityManager { * running its code, {@link RunningAppProcessInfo#IMPORTANCE_GONE} is returned. * @hide */ - @SystemApi @TestApi + @SystemApi @RequiresPermission(Manifest.permission.PACKAGE_USAGE_STATS) public @RunningAppProcessInfo.Importance int getUidImportance(int uid) { try { @@ -3644,7 +3645,7 @@ public class ActivityManager { * {@link #addOnUidImportanceListener}. * @hide */ - @SystemApi @TestApi + @SystemApi public interface OnUidImportanceListener { /** * The importance if a given uid has changed. Will be one of the importance @@ -3675,7 +3676,7 @@ public class ActivityManager { * {@link android.Manifest.permission#PACKAGE_USAGE_STATS}. * @hide */ - @SystemApi @TestApi + @SystemApi @RequiresPermission(Manifest.permission.PACKAGE_USAGE_STATS) public void addOnUidImportanceListener(OnUidImportanceListener listener, @RunningAppProcessInfo.Importance int importanceCutpoint) { @@ -3704,7 +3705,7 @@ public class ActivityManager { * @throws IllegalArgumentException If the listener is not registered. * @hide */ - @SystemApi @TestApi + @SystemApi @RequiresPermission(Manifest.permission.PACKAGE_USAGE_STATS) public void removeOnUidImportanceListener(OnUidImportanceListener listener) { synchronized (this) { @@ -3846,7 +3847,7 @@ public class ActivityManager { * @see #forceStopPackageAsUser(String, int) * @hide */ - @SystemApi @TestApi + @SystemApi @RequiresPermission(Manifest.permission.FORCE_STOP_PACKAGES) public void forceStopPackage(String packageName) { forceStopPackageAsUser(packageName, mContext.getUserId()); @@ -4185,7 +4186,6 @@ public class ActivityManager { * @hide */ @SystemApi - @TestApi @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean switchUser(@NonNull UserHandle user) { if (user == null) { @@ -4618,7 +4618,7 @@ public class ActivityManager { * * @hide */ - @SystemApi @TestApi + @SystemApi @RequiresPermission(Manifest.permission.FORCE_STOP_PACKAGES) public void killProcessesWhenImperceptible(@NonNull int[] pids, @NonNull String reason) { try { @@ -4752,31 +4752,43 @@ public class ActivityManager { } /** - * Register with {@link HomeVisibilityObserver} with ActivityManager. - * TODO: b/144351078 expose as SystemApi + * Register to be notified when the visibility of the home screen changes. + * + * @param executor The executor on which the listener should be called. + * @param listener The listener that is called when home visibility changes. * @hide */ - public void registerHomeVisibilityObserver(@NonNull HomeVisibilityObserver observer) { - Preconditions.checkNotNull(observer); + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) + @TestApi + @RequiresPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER) + public void addHomeVisibilityListener(@NonNull Executor executor, + @NonNull HomeVisibilityListener listener) { + Preconditions.checkNotNull(listener); + Preconditions.checkNotNull(executor); try { - observer.init(mContext, this); - getService().registerProcessObserver(observer.mObserver); + listener.init(mContext, executor, this); + getService().registerProcessObserver(listener.mObserver); // Notify upon first registration. - observer.onHomeVisibilityChanged(observer.mIsHomeActivityVisible); + executor.execute(() -> + listener.onHomeVisibilityChanged(listener.mIsHomeActivityVisible)); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** - * Unregister with {@link HomeVisibilityObserver} with ActivityManager. - * TODO: b/144351078 expose as SystemApi + * Removes a listener that was previously added with {@link #addHomeVisibilityListener}. + * + * @param listener The listener that was previously added. * @hide */ - public void unregisterHomeVisibilityObserver(@NonNull HomeVisibilityObserver observer) { - Preconditions.checkNotNull(observer); + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) + @TestApi + @RequiresPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER) + public void removeHomeVisibilityListener(@NonNull HomeVisibilityListener listener) { + Preconditions.checkNotNull(listener); try { - getService().unregisterProcessObserver(observer.mObserver); + getService().unregisterProcessObserver(listener.mObserver); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java index 3748a33568e2..ef146b118c33 100644 --- a/core/java/android/app/ActivityOptions.java +++ b/core/java/android/app/ActivityOptions.java @@ -37,6 +37,7 @@ import android.graphics.Rect; import android.hardware.HardwareBuffer; import android.os.Bundle; import android.os.Handler; +import android.os.IBinder; import android.os.IRemoteCallback; import android.os.Parcel; import android.os.Parcelable; @@ -306,6 +307,12 @@ public class ActivityOptions { private static final String KEY_REMOTE_ANIMATION_ADAPTER = "android:activity.remoteAnimationAdapter"; + /** + * @see #setLaunchCookie + * @hide + */ + private static final String KEY_LAUNCH_COOKIE = "android.activity.launchCookie"; + /** @hide */ public static final int ANIM_UNDEFINED = -1; /** @hide */ @@ -381,6 +388,7 @@ public class ActivityOptions { private Bundle mAppVerificationBundle; private IAppTransitionAnimationSpecsFuture mSpecsFuture; private RemoteAnimationAdapter mRemoteAnimationAdapter; + private IBinder mLaunchCookie; /** * Create an ActivityOptions specifying a custom animation to run when @@ -1071,6 +1079,7 @@ public class ActivityOptions { KEY_SPECS_FUTURE)); } mRemoteAnimationAdapter = opts.getParcelable(KEY_REMOTE_ANIMATION_ADAPTER); + mLaunchCookie = opts.getBinder(KEY_LAUNCH_COOKIE); } /** @@ -1496,6 +1505,25 @@ public class ActivityOptions { } /** + * Sets a launch cookie that can be used to track the activity and task that are launch as a + * result of this option. + * + * @hide + */ + public void setLaunchCookie(IBinder launchCookie) { + mLaunchCookie = launchCookie; + } + + /** + * @return The launch tracking cookie if set or {@code null} otherwise. + * + * @hide + */ + public IBinder getLaunchCookie() { + return mLaunchCookie; + } + + /** * Update the current values in this ActivityOptions from those supplied * in <var>otherOptions</var>. Any values * defined in <var>otherOptions</var> replace those in the base options. @@ -1717,6 +1745,9 @@ public class ActivityOptions { if (mRemoteAnimationAdapter != null) { b.putParcelable(KEY_REMOTE_ANIMATION_ADAPTER, mRemoteAnimationAdapter); } + if (mLaunchCookie != null) { + b.putBinder(KEY_LAUNCH_COOKIE, mLaunchCookie); + } return b; } diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 8f7116cac81e..4ea2aacdf7c2 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -435,7 +435,7 @@ public final class ActivityThread extends ClientTransactionHandler { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (o instanceof ProviderKey) { final ProviderKey other = (ProviderKey) o; return Objects.equals(authority, other.authority) && userId == other.userId; @@ -2791,7 +2791,7 @@ public final class ActivityThread extends ClientTransactionHandler { memInfo.getTotalPrivateDirty(), memInfo.getTotalPrivateClean(), memInfo.hasSwappedOutPss ? memInfo.getTotalSwappedOutPss() : - memInfo.getTotalSwappedOut(), memInfo.getTotalPss(), + memInfo.getTotalSwappedOut(), memInfo.getTotalRss(), nativeMax+dalvikMax, nativeAllocated+dalvikAllocated, nativeFree+dalvikFree); } @@ -3631,7 +3631,7 @@ public final class ActivityThread extends ClientTransactionHandler { TAG, "Handling launch of " + r); // Initialize before creating the activity - if (!ThreadedRenderer.sRendererDisabled + if (ThreadedRenderer.sRendererEnabled && (r.activityInfo.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0) { HardwareRenderer.preload(); } @@ -7417,14 +7417,7 @@ public final class ActivityThread extends ClientTransactionHandler { @UnsupportedAppUsage public static ActivityThread systemMain() { - // The system process on low-memory devices do not get to use hardware - // accelerated drawing, since this can add too much overhead to the - // process. - if (!ActivityManager.isHighEndGfx()) { - ThreadedRenderer.disable(true); - } else { - ThreadedRenderer.enableForegroundTrimming(); - } + ThreadedRenderer.initForSystemProcess(); ActivityThread thread = new ActivityThread(); thread.attach(true, 0); return thread; diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java index c5bc3564ceab..f3b37891876d 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -461,7 +461,6 @@ public class AppOpsManager { * state the more important the UID is for the user. * @hide */ - @TestApi @SystemApi public static final int UID_STATE_PERSISTENT = 100; @@ -470,7 +469,6 @@ public class AppOpsManager { * state the more important the UID is for the user. * @hide */ - @TestApi @SystemApi public static final int UID_STATE_TOP = 200; @@ -482,7 +480,6 @@ public class AppOpsManager { * @hide * @deprecated */ - @TestApi @SystemApi @Deprecated public static final int UID_STATE_FOREGROUND_SERVICE_LOCATION = 300; @@ -492,7 +489,6 @@ public class AppOpsManager { * state the more important the UID is for the user. * @hide */ - @TestApi @SystemApi public static final int UID_STATE_FOREGROUND_SERVICE = 400; @@ -501,7 +497,6 @@ public class AppOpsManager { * state the more important the UID is for the user. * @hide */ - @TestApi @SystemApi public static final int UID_STATE_FOREGROUND = 500; @@ -517,7 +512,6 @@ public class AppOpsManager { * state the more important the UID is for the user. * @hide */ - @TestApi @SystemApi public static final int UID_STATE_BACKGROUND = 600; @@ -526,7 +520,6 @@ public class AppOpsManager { * state the more important the UID is for the user. * @hide */ - @TestApi @SystemApi public static final int UID_STATE_CACHED = 700; @@ -604,7 +597,6 @@ public class AppOpsManager { * * @hide */ - @TestApi @SystemApi public static final int OP_FLAG_SELF = 0x1; @@ -615,7 +607,6 @@ public class AppOpsManager { * * @hide */ - @TestApi @SystemApi public static final int OP_FLAG_TRUSTED_PROXY = 0x2; @@ -626,7 +617,6 @@ public class AppOpsManager { * * @hide */ - @TestApi @SystemApi public static final int OP_FLAG_UNTRUSTED_PROXY = 0x4; @@ -637,7 +627,6 @@ public class AppOpsManager { * * @hide */ - @TestApi @SystemApi public static final int OP_FLAG_TRUSTED_PROXIED = 0x8; @@ -648,7 +637,6 @@ public class AppOpsManager { * * @hide */ - @TestApi @SystemApi public static final int OP_FLAG_UNTRUSTED_PROXIED = 0x10; @@ -660,7 +648,6 @@ public class AppOpsManager { * * @hide */ - @TestApi @SystemApi public static final int OP_FLAGS_ALL = OP_FLAG_SELF @@ -1185,7 +1172,7 @@ public class AppOpsManager { public static final String OPSTR_GET_USAGE_STATS = "android:get_usage_stats"; /** Activate a VPN connection without user intervention. @hide */ - @SystemApi @TestApi + @SystemApi public static final String OPSTR_ACTIVATE_VPN = "android:activate_vpn"; /** Allows an application to read the user's contacts data. */ @@ -1267,7 +1254,7 @@ public class AppOpsManager { public static final String OPSTR_WRITE_SETTINGS = "android:write_settings"; /** @hide Get device accounts. */ - @SystemApi @TestApi + @SystemApi public static final String OPSTR_GET_ACCOUNTS = "android:get_accounts"; public static final String OPSTR_READ_PHONE_NUMBERS @@ -1276,7 +1263,7 @@ public class AppOpsManager { public static final String OPSTR_PICTURE_IN_PICTURE = "android:picture_in_picture"; /** @hide */ - @SystemApi @TestApi + @SystemApi public static final String OPSTR_INSTANT_APP_START_FOREGROUND = "android:instant_app_start_foreground"; /** Answer incoming phone calls */ @@ -1286,129 +1273,129 @@ public class AppOpsManager { * Accept call handover * @hide */ - @SystemApi @TestApi + @SystemApi public static final String OPSTR_ACCEPT_HANDOVER = "android:accept_handover"; /** @hide */ - @SystemApi @TestApi + @SystemApi public static final String OPSTR_GPS = "android:gps"; /** @hide */ - @SystemApi @TestApi + @SystemApi public static final String OPSTR_VIBRATE = "android:vibrate"; /** @hide */ - @SystemApi @TestApi + @SystemApi public static final String OPSTR_WIFI_SCAN = "android:wifi_scan"; /** @hide */ - @SystemApi @TestApi + @SystemApi public static final String OPSTR_POST_NOTIFICATION = "android:post_notification"; /** @hide */ - @SystemApi @TestApi + @SystemApi public static final String OPSTR_NEIGHBORING_CELLS = "android:neighboring_cells"; /** @hide */ - @SystemApi @TestApi + @SystemApi public static final String OPSTR_WRITE_SMS = "android:write_sms"; /** @hide */ - @SystemApi @TestApi + @SystemApi public static final String OPSTR_RECEIVE_EMERGENCY_BROADCAST = "android:receive_emergency_broadcast"; /** @hide */ - @SystemApi @TestApi + @SystemApi public static final String OPSTR_READ_ICC_SMS = "android:read_icc_sms"; /** @hide */ - @SystemApi @TestApi + @SystemApi public static final String OPSTR_WRITE_ICC_SMS = "android:write_icc_sms"; /** @hide */ - @SystemApi @TestApi + @SystemApi public static final String OPSTR_ACCESS_NOTIFICATIONS = "android:access_notifications"; /** @hide */ - @SystemApi @TestApi + @SystemApi public static final String OPSTR_PLAY_AUDIO = "android:play_audio"; /** @hide */ - @SystemApi @TestApi + @SystemApi public static final String OPSTR_READ_CLIPBOARD = "android:read_clipboard"; /** @hide */ - @SystemApi @TestApi + @SystemApi public static final String OPSTR_WRITE_CLIPBOARD = "android:write_clipboard"; /** @hide */ - @SystemApi @TestApi + @SystemApi public static final String OPSTR_TAKE_MEDIA_BUTTONS = "android:take_media_buttons"; /** @hide */ - @SystemApi @TestApi + @SystemApi public static final String OPSTR_TAKE_AUDIO_FOCUS = "android:take_audio_focus"; /** @hide */ - @SystemApi @TestApi + @SystemApi public static final String OPSTR_AUDIO_MASTER_VOLUME = "android:audio_master_volume"; /** @hide */ - @SystemApi @TestApi + @SystemApi public static final String OPSTR_AUDIO_VOICE_VOLUME = "android:audio_voice_volume"; /** @hide */ - @SystemApi @TestApi + @SystemApi public static final String OPSTR_AUDIO_RING_VOLUME = "android:audio_ring_volume"; /** @hide */ - @SystemApi @TestApi + @SystemApi public static final String OPSTR_AUDIO_MEDIA_VOLUME = "android:audio_media_volume"; /** @hide */ - @SystemApi @TestApi + @SystemApi public static final String OPSTR_AUDIO_ALARM_VOLUME = "android:audio_alarm_volume"; /** @hide */ - @SystemApi @TestApi + @SystemApi public static final String OPSTR_AUDIO_NOTIFICATION_VOLUME = "android:audio_notification_volume"; /** @hide */ - @SystemApi @TestApi + @SystemApi public static final String OPSTR_AUDIO_BLUETOOTH_VOLUME = "android:audio_bluetooth_volume"; /** @hide */ - @SystemApi @TestApi + @SystemApi public static final String OPSTR_WAKE_LOCK = "android:wake_lock"; /** @hide */ - @SystemApi @TestApi + @SystemApi public static final String OPSTR_MUTE_MICROPHONE = "android:mute_microphone"; /** @hide */ - @SystemApi @TestApi + @SystemApi public static final String OPSTR_TOAST_WINDOW = "android:toast_window"; /** @hide */ - @SystemApi @TestApi + @SystemApi public static final String OPSTR_PROJECT_MEDIA = "android:project_media"; /** @hide */ - @SystemApi @TestApi + @SystemApi public static final String OPSTR_WRITE_WALLPAPER = "android:write_wallpaper"; /** @hide */ - @SystemApi @TestApi + @SystemApi public static final String OPSTR_ASSIST_STRUCTURE = "android:assist_structure"; /** @hide */ - @SystemApi @TestApi + @SystemApi public static final String OPSTR_ASSIST_SCREENSHOT = "android:assist_screenshot"; /** @hide */ - @SystemApi @TestApi + @SystemApi public static final String OPSTR_TURN_SCREEN_ON = "android:turn_screen_on"; /** @hide */ - @SystemApi @TestApi + @SystemApi public static final String OPSTR_RUN_IN_BACKGROUND = "android:run_in_background"; /** @hide */ - @SystemApi @TestApi + @SystemApi public static final String OPSTR_AUDIO_ACCESSIBILITY_VOLUME = "android:audio_accessibility_volume"; /** @hide */ - @SystemApi @TestApi + @SystemApi public static final String OPSTR_REQUEST_INSTALL_PACKAGES = "android:request_install_packages"; /** @hide */ - @SystemApi @TestApi + @SystemApi public static final String OPSTR_RUN_ANY_IN_BACKGROUND = "android:run_any_in_background"; /** @hide */ - @SystemApi @TestApi + @SystemApi public static final String OPSTR_CHANGE_WIFI_STATE = "android:change_wifi_state"; /** @hide */ - @SystemApi @TestApi + @SystemApi public static final String OPSTR_REQUEST_DELETE_PACKAGES = "android:request_delete_packages"; /** @hide */ - @SystemApi @TestApi + @SystemApi public static final String OPSTR_BIND_ACCESSIBILITY_SERVICE = "android:bind_accessibility_service"; /** @hide */ - @SystemApi @TestApi + @SystemApi public static final String OPSTR_MANAGE_IPSEC_TUNNELS = "android:manage_ipsec_tunnels"; /** @hide */ - @SystemApi @TestApi + @SystemApi public static final String OPSTR_START_FOREGROUND = "android:start_foreground"; /** @hide */ public static final String OPSTR_BLUETOOTH_SCAN = "android:bluetooth_scan"; @@ -1424,25 +1411,25 @@ public class AppOpsManager { "android:sms_financial_transactions"; /** @hide Read media of audio type. */ - @SystemApi @TestApi + @SystemApi public static final String OPSTR_READ_MEDIA_AUDIO = "android:read_media_audio"; /** @hide Write media of audio type. */ - @SystemApi @TestApi + @SystemApi public static final String OPSTR_WRITE_MEDIA_AUDIO = "android:write_media_audio"; /** @hide Read media of video type. */ - @SystemApi @TestApi + @SystemApi public static final String OPSTR_READ_MEDIA_VIDEO = "android:read_media_video"; /** @hide Write media of video type. */ - @SystemApi @TestApi + @SystemApi public static final String OPSTR_WRITE_MEDIA_VIDEO = "android:write_media_video"; /** @hide Read media of image type. */ - @SystemApi @TestApi + @SystemApi public static final String OPSTR_READ_MEDIA_IMAGES = "android:read_media_images"; /** @hide Write media of image type. */ - @SystemApi @TestApi + @SystemApi public static final String OPSTR_WRITE_MEDIA_IMAGES = "android:write_media_images"; /** @hide Has a legacy (non-isolated) view of storage. */ - @SystemApi @TestApi + @SystemApi public static final String OPSTR_LEGACY_STORAGE = "android:legacy_storage"; /** @hide Read location metadata from media */ public static final String OPSTR_ACCESS_MEDIA_LOCATION = "android:access_media_location"; @@ -1456,7 +1443,6 @@ public class AppOpsManager { public static final String OPSTR_QUERY_ALL_PACKAGES = "android:query_all_packages"; /** @hide Access all external storage */ @SystemApi - @TestApi public static final String OPSTR_MANAGE_EXTERNAL_STORAGE = "android:manage_external_storage"; @@ -2683,7 +2669,6 @@ public class AppOpsManager { * * @hide */ - @TestApi @SystemApi public static int opToDefaultMode(@NonNull String appOp) { return opToDefaultMode(strOpToOp(appOp)); @@ -2775,7 +2760,6 @@ public class AppOpsManager { * Class holding all of the operation information associated with an app. * @hide */ - @TestApi @SystemApi public static final class PackageOps implements Parcelable { private final String mPackageName; @@ -2854,7 +2838,6 @@ public class AppOpsManager { * * @hide */ - @TestApi @SystemApi // @DataClass(genHiddenConstructor = true, genHiddenCopyConstructor = true) // genHiddenCopyConstructor does not work for @hide @SystemApi classes @@ -3222,7 +3205,6 @@ public class AppOpsManager { * * @hide */ - @TestApi @SystemApi @Immutable // @DataClass(genHiddenConstructor = true) codegen verifier is broken @@ -3796,7 +3778,6 @@ public class AppOpsManager { * * @hide */ - @TestApi @Immutable @SystemApi // @DataClass(genHiddenConstructor = true) codegen verifier is broken @@ -4474,7 +4455,6 @@ public class AppOpsManager { * @hide */ @Immutable - @TestApi @SystemApi public static final class HistoricalOpsRequest { private final int mUid; @@ -4505,7 +4485,6 @@ public class AppOpsManager { * * @hide */ - @TestApi @SystemApi public static final class Builder { private int mUid = Process.INVALID_UID; @@ -4643,7 +4622,6 @@ public class AppOpsManager { * * @hide */ - @TestApi @SystemApi public static final class HistoricalOps implements Parcelable { private long mBeginTimeMillis; @@ -5079,7 +5057,6 @@ public class AppOpsManager { * * @hide */ - @TestApi @SystemApi public static final class HistoricalUidOps implements Parcelable { private final int mUid; @@ -5333,7 +5310,6 @@ public class AppOpsManager { * * @hide */ - @TestApi @SystemApi public static final class HistoricalPackageOps implements Parcelable { private final @NonNull String mPackageName; @@ -5664,7 +5640,6 @@ public class AppOpsManager { * * @hide */ - @TestApi @SystemApi /* codegen verifier cannot deal with nested class parameters @DataClass(genHiddenConstructor = true, @@ -5975,7 +5950,6 @@ public class AppOpsManager { * * @hide */ - @TestApi @SystemApi public static final class HistoricalOp implements Parcelable { private final int mOp; @@ -6612,7 +6586,6 @@ public class AppOpsManager { * * @hide */ - @TestApi @SystemApi @RequiresPermission(android.Manifest.permission.GET_APP_OPS_STATS) public @NonNull List<AppOpsManager.PackageOps> getOpsForPackage(int uid, @@ -6647,7 +6620,6 @@ public class AppOpsManager { * * @hide */ - @TestApi @SystemApi @RequiresPermission(android.Manifest.permission.GET_APP_OPS_STATS) public void getHistoricalOps(@NonNull HistoricalOpsRequest request, @@ -6756,7 +6728,6 @@ public class AppOpsManager { * @hide */ @SystemApi - @TestApi @RequiresPermission(android.Manifest.permission.MANAGE_APP_OPS_MODES) public void setUidMode(@NonNull String appOp, int uid, @Mode int mode) { try { @@ -6810,7 +6781,6 @@ public class AppOpsManager { * be changed. * @hide */ - @TestApi @SystemApi @RequiresPermission(android.Manifest.permission.MANAGE_APP_OPS_MODES) public void setMode(@NonNull String op, int uid, @Nullable String packageName, @@ -8773,7 +8743,6 @@ public class AppOpsManager { * @hide */ @SystemApi - @TestApi @RequiresPermission(Manifest.permission.GET_APP_OPS_STATS) public @Nullable RuntimeAppOpAccessMessage collectRuntimeAppOpAccessMessage() { try { @@ -8788,7 +8757,6 @@ public class AppOpsManager { * @hide */ @SystemApi - @TestApi public static String[] getOpStrs() { return Arrays.copyOf(sOpToString, sOpToString.length); } diff --git a/core/java/android/app/ApplicationExitInfo.java b/core/java/android/app/ApplicationExitInfo.java index e7b3e14bfda7..d716a3cf5445 100644 --- a/core/java/android/app/ApplicationExitInfo.java +++ b/core/java/android/app/ApplicationExitInfo.java @@ -1164,7 +1164,7 @@ public final class ApplicationExitInfo implements Parcelable { } @Override - public boolean equals(Object other) { + public boolean equals(@Nullable Object other) { if (other == null || !(other instanceof ApplicationExitInfo)) { return false; } diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java index 7b25e25f3ff4..c6b52c1ea705 100644 --- a/core/java/android/app/ApplicationPackageManager.java +++ b/core/java/android/app/ApplicationPackageManager.java @@ -667,7 +667,7 @@ public class ApplicationPackageManager extends PackageManager { name, version); } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (o instanceof HasSystemFeatureQuery) { HasSystemFeatureQuery r = (HasSystemFeatureQuery) o; return Objects.equals(name, r.name) && version == r.version; @@ -1050,7 +1050,7 @@ public class ApplicationPackageManager extends PackageManager { * are handled first. */ @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (o instanceof GetPackagesForUidResult) { String [] r = ((GetPackagesForUidResult) o).mValue; String [] l = mValue; @@ -2083,7 +2083,7 @@ public class ApplicationPackageManager extends PackageManager { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; diff --git a/core/java/android/app/AutomaticZenRule.java b/core/java/android/app/AutomaticZenRule.java index 7180c01143a5..c3272c1a770d 100644 --- a/core/java/android/app/AutomaticZenRule.java +++ b/core/java/android/app/AutomaticZenRule.java @@ -282,7 +282,7 @@ public final class AutomaticZenRule implements Parcelable { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (!(o instanceof AutomaticZenRule)) return false; if (o == this) return true; final AutomaticZenRule other = (AutomaticZenRule) o; diff --git a/core/java/android/app/DirectAction.java b/core/java/android/app/DirectAction.java index 0268f7ca2b73..b0ed490369ad 100644 --- a/core/java/android/app/DirectAction.java +++ b/core/java/android/app/DirectAction.java @@ -132,7 +132,7 @@ public final class DirectAction implements Parcelable { } @Override - public boolean equals(Object other) { + public boolean equals(@Nullable Object other) { if (other == null) { return false; } diff --git a/core/java/android/app/Fragment.java b/core/java/android/app/Fragment.java index f7fb3c34ce1b..ce4109cd44a3 100644 --- a/core/java/android/app/Fragment.java +++ b/core/java/android/app/Fragment.java @@ -596,7 +596,7 @@ public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListene /** * Subclasses can not override equals(). */ - @Override final public boolean equals(Object o) { + @Override final public boolean equals(@Nullable Object o) { return super.equals(o); } diff --git a/core/java/android/app/HomeVisibilityObserver.java b/core/java/android/app/HomeVisibilityListener.java index 8422c6f6f872..c6e5699c5fe2 100644 --- a/core/java/android/app/HomeVisibilityObserver.java +++ b/core/java/android/app/HomeVisibilityListener.java @@ -16,49 +16,56 @@ package android.app; +import android.annotation.SuppressLint; +import android.annotation.SystemApi; +import android.annotation.TestApi; import android.content.Context; -import android.content.Intent; -import android.content.pm.PackageManager; -import android.content.pm.ResolveInfo; +import android.os.Binder; +import java.util.ArrayList; import java.util.List; +import java.util.Objects; +import java.util.concurrent.Executor; /** - * An observer / callback to create and register by - * {@link ActivityManager#registerHomeVisibilityObserver} so that it's triggered when - * visibility of home page changes. - * TODO: b/144351078 expose as SystemApi + * A listener that will be invoked when the visibility of the home screen changes. + * Register this callback via {@link ActivityManager#addHomeVisibilityListener} * @hide */ -public abstract class HomeVisibilityObserver { +// This is a single-method listener that needs a bunch of supporting code, so it can't be an +// interface +@SuppressLint("ListenerInterface") +@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) +@TestApi +public abstract class HomeVisibilityListener { private Context mContext; private ActivityManager mActivityManager; + private Executor mExecutor; /** @hide */ - IProcessObserver.Stub mObserver; + android.app.IProcessObserver.Stub mObserver; /** @hide */ boolean mIsHomeActivityVisible; /** @hide */ - void init(Context context, ActivityManager activityManager) { + void init(Context context, Executor executor, ActivityManager activityManager) { mContext = context; mActivityManager = activityManager; mIsHomeActivityVisible = isHomeActivityVisible(); + mExecutor = executor; } /** - * The API that needs implemented and will be triggered when activity on home page changes. + * Called when the visibility of the home screen changes. + * + * @param isHomeActivityVisible Whether the home screen activity is now visible. */ public abstract void onHomeVisibilityChanged(boolean isHomeActivityVisible); - public HomeVisibilityObserver() { - mObserver = new IProcessObserver.Stub() { + public HomeVisibilityListener() { + mObserver = new android.app.IProcessObserver.Stub() { @Override public void onForegroundActivitiesChanged(int pid, int uid, boolean fg) { - boolean isHomeActivityVisible = isHomeActivityVisible(); - if (mIsHomeActivityVisible != isHomeActivityVisible) { - mIsHomeActivityVisible = isHomeActivityVisible; - onHomeVisibilityChanged(mIsHomeActivityVisible); - } + refreshHomeVisibility(); } @Override @@ -67,6 +74,17 @@ public abstract class HomeVisibilityObserver { @Override public void onProcessDied(int pid, int uid) { + refreshHomeVisibility(); + } + + private void refreshHomeVisibility() { + boolean isHomeActivityVisible = isHomeActivityVisible(); + if (mIsHomeActivityVisible != isHomeActivityVisible) { + mIsHomeActivityVisible = isHomeActivityVisible; + Binder.withCleanCallingIdentity(() -> + mExecutor.execute(() -> + onHomeVisibilityChanged(mIsHomeActivityVisible))); + } } }; } @@ -83,12 +101,9 @@ public abstract class HomeVisibilityObserver { } // We can assume that the screen is idle if the home application is in the foreground. - final Intent intent = new Intent(Intent.ACTION_MAIN, null); - intent.addCategory(Intent.CATEGORY_HOME); - - ResolveInfo info = mContext.getPackageManager().resolveActivity(intent, - PackageManager.MATCH_DEFAULT_ONLY); - if (info != null && top.equals(info.activityInfo.packageName)) { + String defaultHomePackage = mContext.getPackageManager() + .getHomeActivities(new ArrayList<>()).getPackageName(); + if (Objects.equals(top, defaultHomePackage)) { return true; } diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl index 66007e5c17d6..75302293088f 100644 --- a/core/java/android/app/IActivityTaskManager.aidl +++ b/core/java/android/app/IActivityTaskManager.aidl @@ -165,7 +165,8 @@ interface IActivityTaskManager { int getTaskForActivity(in IBinder token, in boolean onlyRoot); /** Finish all activities that were started for result from the specified activity. */ void finishSubActivity(in IBinder token, in String resultWho, int requestCode); - ParceledListSlice getRecentTasks(int maxNum, int flags, int userId); + ParceledListSlice<ActivityManager.RecentTaskInfo> getRecentTasks(int maxNum, int flags, + int userId); boolean willActivityBeVisible(in IBinder token); void setRequestedOrientation(in IBinder token, int requestedOrientation); int getRequestedOrientation(in IBinder token); diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index 54380629e4ca..60e7f0bc91c0 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -8457,9 +8457,7 @@ public class Notification implements Parcelable Action action, StandardTemplateParams p) { final boolean tombstone = (action.actionIntent == null); container.setViewVisibility(buttonId, View.VISIBLE); - if (buttonId != R.id.media_seamless) { - container.setImageViewIcon(buttonId, action.getIcon()); - } + container.setImageViewIcon(buttonId, action.getIcon()); // If the action buttons should not be tinted, then just use the default // notification color. Otherwise, just use the passed-in color. @@ -8513,10 +8511,6 @@ public class Notification implements Parcelable view.setViewVisibility(MEDIA_BUTTON_IDS[i], View.GONE); } } - bindMediaActionButton(view, R.id.media_seamless, new Action( - R.drawable.ic_media_seamless, mBuilder.mContext.getString( - com.android.internal.R.string.ext_media_seamless_action), null), p); - view.setViewVisibility(R.id.media_seamless, View.GONE); handleImage(view); // handle the content margin int endMargin = R.dimen.notification_content_margin_end; @@ -8553,10 +8547,6 @@ public class Notification implements Parcelable big.setViewVisibility(MEDIA_BUTTON_IDS[i], View.GONE); } } - bindMediaActionButton(big, R.id.media_seamless, new Action(R.drawable.ic_media_seamless, - mBuilder.mContext.getString( - com.android.internal.R.string.ext_media_seamless_action), null), p); - big.setViewVisibility(R.id.media_seamless, View.GONE); handleImage(big); return big; } diff --git a/core/java/android/app/NotificationChannel.java b/core/java/android/app/NotificationChannel.java index c827e601ce88..f0e457e9691a 100644 --- a/core/java/android/app/NotificationChannel.java +++ b/core/java/android/app/NotificationChannel.java @@ -134,7 +134,6 @@ public final class NotificationChannel implements Parcelable { * @hide */ @SystemApi - @TestApi public static final int USER_LOCKED_SOUND = 0x00000020; /** @@ -391,7 +390,6 @@ public final class NotificationChannel implements Parcelable { * @hide */ @SystemApi - @TestApi public void setBlockable(boolean blockable) { mBlockableSystem = blockable; } @@ -1175,7 +1173,7 @@ public final class NotificationChannel implements Parcelable { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; NotificationChannel that = (NotificationChannel) o; diff --git a/core/java/android/app/NotificationChannelGroup.java b/core/java/android/app/NotificationChannelGroup.java index 403fb3e3727c..ec7fa332b23f 100644 --- a/core/java/android/app/NotificationChannelGroup.java +++ b/core/java/android/app/NotificationChannelGroup.java @@ -15,6 +15,7 @@ */ package android.app; +import android.annotation.Nullable; import android.annotation.SystemApi; import android.annotation.TestApi; import android.compat.annotation.UnsupportedAppUsage; @@ -291,7 +292,7 @@ public final class NotificationChannelGroup implements Parcelable { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; NotificationChannelGroup that = (NotificationChannelGroup) o; diff --git a/core/java/android/app/NotificationHistory.java b/core/java/android/app/NotificationHistory.java index 55fff8b2f074..0c8188b9a51e 100644 --- a/core/java/android/app/NotificationHistory.java +++ b/core/java/android/app/NotificationHistory.java @@ -116,7 +116,7 @@ public final class NotificationHistory implements Parcelable { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; HistoricalNotification that = (HistoricalNotification) o; diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java index fe8936654aa4..f3bd04cd132e 100644 --- a/core/java/android/app/NotificationManager.java +++ b/core/java/android/app/NotificationManager.java @@ -1430,7 +1430,6 @@ public class NotificationManager { * @hide */ @SystemApi - @TestApi public boolean isNotificationAssistantAccessGranted(@NonNull ComponentName assistant) { INotificationManager service = getService(); try { @@ -1466,7 +1465,6 @@ public class NotificationManager { * @hide */ @SystemApi - @TestApi public @NonNull @Adjustment.Keys List<String> getAllowedAssistantAdjustments() { INotificationManager service = getService(); try { @@ -1567,8 +1565,19 @@ public class NotificationManager { } } - /** @hide */ - public void setNotificationListenerAccessGranted(ComponentName listener, boolean granted) { + /** + * Grants/revokes Notification Listener access to the given component for current user. + * To grant access for a particular user, obtain this service by using the {@link Context} + * provided by {@link Context#createPackageContextAsUser} + * + * @param listener Name of component to grant/revoke access + * @param granted Grant/revoke access + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.MANAGE_NOTIFICATION_LISTENERS) + public void setNotificationListenerAccessGranted( + @NonNull ComponentName listener, boolean granted) { INotificationManager service = getService(); try { service.setNotificationListenerAccessGranted(listener, granted); @@ -1599,7 +1608,6 @@ public class NotificationManager { * @hide */ @SystemApi - @TestApi public void setNotificationAssistantAccessGranted(@Nullable ComponentName assistant, boolean granted) { INotificationManager service = getService(); @@ -1610,6 +1618,20 @@ public class NotificationManager { } } + /** + * Gets the list of enabled notification listener components for current user. + * To query for a particular user, obtain this service by using the {@link Context} + * provided by {@link Context#createPackageContextAsUser} + * + * @return the list of {@link ComponentName}s of the notification listeners + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.MANAGE_NOTIFICATION_LISTENERS) + public @NonNull List<ComponentName> getEnabledNotificationListeners() { + return getEnabledNotificationListeners(mContext.getUserId()); + } + /** @hide */ public List<ComponentName> getEnabledNotificationListeners(int userId) { INotificationManager service = getService(); @@ -1622,7 +1644,6 @@ public class NotificationManager { /** @hide */ @SystemApi - @TestApi public @Nullable ComponentName getAllowedNotificationAssistant() { INotificationManager service = getService(); try { @@ -2000,7 +2021,7 @@ public class NotificationManager { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (!(o instanceof Policy)) return false; if (o == this) return true; final Policy other = (Policy) o; diff --git a/core/java/android/app/PendingIntent.java b/core/java/android/app/PendingIntent.java index e8937a8da911..acc42dbcb52a 100644 --- a/core/java/android/app/PendingIntent.java +++ b/core/java/android/app/PendingIntent.java @@ -363,6 +363,7 @@ public final class PendingIntent implements Parcelable { * parameters. May return null only if {@link #FLAG_NO_CREATE} has been * supplied. */ + @SuppressWarnings("AndroidFrameworkPendingIntentMutability") public static PendingIntent getActivity(Context context, int requestCode, Intent intent, @Flags int flags) { return getActivity(context, requestCode, intent, flags, null); @@ -489,6 +490,7 @@ public final class PendingIntent implements Parcelable { * parameters. May return null only if {@link #FLAG_NO_CREATE} has been * supplied. */ + @SuppressWarnings("AndroidFrameworkPendingIntentMutability") public static PendingIntent getActivities(Context context, int requestCode, @NonNull Intent[] intents, @Flags int flags) { return getActivities(context, requestCode, intents, flags, null); @@ -611,6 +613,7 @@ public final class PendingIntent implements Parcelable { * parameters. May return null only if {@link #FLAG_NO_CREATE} has been * supplied. */ + @SuppressWarnings("AndroidFrameworkPendingIntentMutability") public static PendingIntent getBroadcast(Context context, int requestCode, Intent intent, @Flags int flags) { return getBroadcastAsUser(context, requestCode, intent, flags, context.getUser()); @@ -1236,7 +1239,7 @@ public final class PendingIntent implements Parcelable { * operation. */ @Override - public boolean equals(Object otherObj) { + public boolean equals(@Nullable Object otherObj) { if (otherObj instanceof PendingIntent) { return mTarget.asBinder().equals(((PendingIntent)otherObj) .mTarget.asBinder()); diff --git a/core/java/android/app/Person.java b/core/java/android/app/Person.java index 63ef2484ca1d..97a794d4e4ea 100644 --- a/core/java/android/app/Person.java +++ b/core/java/android/app/Person.java @@ -138,7 +138,7 @@ public final class Person implements Parcelable { } @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { if (obj instanceof Person) { final Person other = (Person) obj; return Objects.equals(mName, other.mName) diff --git a/core/java/android/app/Presentation.java b/core/java/android/app/Presentation.java index 7a18b8120d7e..ad903642c908 100644 --- a/core/java/android/app/Presentation.java +++ b/core/java/android/app/Presentation.java @@ -16,30 +16,28 @@ package android.app; -import static android.content.Context.DISPLAY_SERVICE; -import static android.content.Context.WINDOW_SERVICE; +import static android.view.WindowManager.LayoutParams.INVALID_WINDOW_TYPE; import static android.view.WindowManager.LayoutParams.TYPE_PRESENTATION; import static android.view.WindowManager.LayoutParams.TYPE_PRIVATE_PRESENTATION; +import android.annotation.NonNull; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.content.res.Resources; import android.hardware.display.DisplayManager; import android.hardware.display.DisplayManager.DisplayListener; -import android.os.Binder; +import android.os.Build; import android.os.Handler; -import android.os.IBinder; -import android.os.Message; -import android.util.DisplayMetrics; -import android.util.Log; +import android.os.Looper; import android.util.TypedValue; import android.view.ContextThemeWrapper; import android.view.Display; import android.view.Gravity; import android.view.Window; import android.view.WindowManager; -import android.view.WindowManagerImpl; +import android.view.WindowManager.LayoutParams.WindowType; +import com.android.internal.util.Preconditions; /** * Base class for presentations. * <p> @@ -153,11 +151,10 @@ import android.view.WindowManagerImpl; public class Presentation extends Dialog { private static final String TAG = "Presentation"; - private static final int MSG_CANCEL = 1; - private final Display mDisplay; private final DisplayManager mDisplayManager; - private final IBinder mToken = new Binder(); + private final Handler mHandler = new Handler(Preconditions.checkNotNull(Looper.myLooper(), + "Presentation must be constructed on a looper thread.")); /** * Creates a new presentation that is attached to the specified display @@ -179,6 +176,11 @@ public class Presentation extends Dialog { * @param outerContext The context of the application that is showing the presentation. * The presentation will create its own context (see {@link #getContext()}) based * on this context and information about the associated display. + * From {@link android.os.Build.VERSION_CODES#S}, the presentation will create its own window + * context based on this context, information about the associated display. Customizing window + * type by {@link Window#setType(int) #getWindow#setType(int)} causes the mismatch of the window + * and the created window context, which leads to + * {@link android.view.WindowManager.InvalidDisplayException} when invoking {@link #show()}. * @param display The display to which the presentation should be attached. * @param theme A style resource describing the theme to use for the window. * See <a href="{@docRoot}guide/topics/resources/available-resources.html#stylesandthemes"> @@ -187,24 +189,53 @@ public class Presentation extends Dialog { * <var>outerContext</var>. If 0, the default presentation theme will be used. */ public Presentation(Context outerContext, Display display, int theme) { - super(createPresentationContext(outerContext, display, theme), theme, false); + this(outerContext, display, theme, INVALID_WINDOW_TYPE); + } - mDisplay = display; - mDisplayManager = (DisplayManager)getContext().getSystemService(DISPLAY_SERVICE); + /** + * Creates a new presentation that is attached to the specified display + * using the optionally specified theme, and override the default window type for the + * presentation. + * @param outerContext The context of the application that is showing the presentation. + * The presentation will create its own context (see {@link #getContext()}) based + * on this context and information about the associated display. + * From {@link android.os.Build.VERSION_CODES#S}, the presentation will create its own window + * context based on this context, information about the associated display and the window type. + * If the window type is not specified, the presentation will choose the default type for the + * presentation. + * @param display The display to which the presentation should be attached. + * @param theme A style resource describing the theme to use for the window. + * See <a href="{@docRoot}guide/topics/resources/available-resources.html#stylesandthemes"> + * Style and Theme Resources</a> for more information about defining and using + * styles. This theme is applied on top of the current theme in + * <var>outerContext</var>. If 0, the default presentation theme will be used. + * @param type Window type. + * + * @hide + */ + public Presentation(@NonNull Context outerContext, @NonNull Display display, int theme, + @WindowType int type) { + super(createPresentationContext(outerContext, display, theme, type), theme, false); - final int windowType = - (display.getFlags() & Display.FLAG_PRIVATE) != 0 ? TYPE_PRIVATE_PRESENTATION - : TYPE_PRESENTATION; + mDisplay = display; + mDisplayManager = getContext().getSystemService(DisplayManager.class); final Window w = getWindow(); final WindowManager.LayoutParams attr = w.getAttributes(); - attr.token = mToken; w.setAttributes(attr); w.setGravity(Gravity.FILL); - w.setType(windowType); + w.setType(getWindowType(type, display)); setCanceledOnTouchOutside(false); } + private static @WindowType int getWindowType(@WindowType int type, @NonNull Display display) { + if (type != INVALID_WINDOW_TYPE) { + return type; + } + return (display.getFlags() & Display.FLAG_PRIVATE) != 0 ? TYPE_PRIVATE_PRESENTATION + : TYPE_PRESENTATION; + } + /** * Gets the {@link Display} that this presentation appears on. * @@ -229,16 +260,6 @@ public class Presentation extends Dialog { protected void onStart() { super.onStart(); mDisplayManager.registerDisplayListener(mDisplayListener, mHandler); - - // Since we were not watching for display changes until just now, there is a - // chance that the display metrics have changed. If so, we will need to - // dismiss the presentation immediately. This case is expected - // to be rare but surprising, so we'll write a log message about it. - if (!isConfigurationStillValid()) { - Log.i(TAG, "Presentation is being dismissed because the " - + "display metrics have changed since it was created."); - mHandler.sendEmptyMessage(MSG_CANCEL); - } } @Override @@ -273,10 +294,6 @@ public class Presentation extends Dialog { * Called by the system when the properties of the {@link Display} to which * the presentation is attached have changed. * - * If the display metrics have changed (for example, if the display has been - * resized or rotated), then the system automatically calls - * {@link #cancel} to dismiss the presentation. - * * @see #getDisplay */ public void onDisplayChanged() { @@ -289,28 +306,16 @@ public class Presentation extends Dialog { private void handleDisplayChanged() { onDisplayChanged(); - - // We currently do not support configuration changes for presentations - // (although we could add that feature with a bit more work). - // If the display metrics have changed in any way then the current configuration - // is invalid and the application must recreate the presentation to get - // a new context. - if (!isConfigurationStillValid()) { - Log.i(TAG, "Presentation is being dismissed because the " - + "display metrics have changed since it was created."); - cancel(); - } } - private boolean isConfigurationStillValid() { - DisplayMetrics dm = new DisplayMetrics(); - mDisplay.getMetrics(dm); - return dm.equalsPhysical(getResources().getDisplayMetrics()); + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, publicAlternatives = "{@code N/A}") + private static Context createPresentationContext(Context outerContext, Display display, + int theme) { + return createPresentationContext(outerContext, display, theme, INVALID_WINDOW_TYPE); } - @UnsupportedAppUsage private static Context createPresentationContext( - Context outerContext, Display display, int theme) { + Context outerContext, Display display, int theme, @WindowType int type) { if (outerContext == null) { throw new IllegalArgumentException("outerContext must not be null"); } @@ -318,31 +323,15 @@ public class Presentation extends Dialog { throw new IllegalArgumentException("display must not be null"); } - Context displayContext = outerContext.createDisplayContext(display); + Context windowContext = outerContext.createDisplayContext(display) + .createWindowContext(getWindowType(type, display), null /* options */); if (theme == 0) { TypedValue outValue = new TypedValue(); - displayContext.getTheme().resolveAttribute( + windowContext.getTheme().resolveAttribute( com.android.internal.R.attr.presentationTheme, outValue, true); theme = outValue.resourceId; } - - // Derive the display's window manager from the outer window manager. - // We do this because the outer window manager have some extra information - // such as the parent window, which is important if the presentation uses - // an application window type. - final WindowManagerImpl outerWindowManager = - (WindowManagerImpl)outerContext.getSystemService(WINDOW_SERVICE); - final WindowManagerImpl displayWindowManager = - outerWindowManager.createPresentationWindowManager(displayContext); - return new ContextThemeWrapper(displayContext, theme) { - @Override - public Object getSystemService(String name) { - if (WINDOW_SERVICE.equals(name)) { - return displayWindowManager; - } - return super.getSystemService(name); - } - }; + return new ContextThemeWrapper(windowContext, theme); } private final DisplayListener mDisplayListener = new DisplayListener() { @@ -364,15 +353,4 @@ public class Presentation extends Dialog { } } }; - - private final Handler mHandler = new Handler() { - @Override - public void handleMessage(Message msg) { - switch (msg.what) { - case MSG_CANCEL: - cancel(); - break; - } - } - }; } diff --git a/core/java/android/app/ProfilerInfo.java b/core/java/android/app/ProfilerInfo.java index d6eb4a83b509..854406cae782 100644 --- a/core/java/android/app/ProfilerInfo.java +++ b/core/java/android/app/ProfilerInfo.java @@ -16,6 +16,7 @@ package android.app; +import android.annotation.Nullable; import android.os.Parcel; import android.os.ParcelFileDescriptor; import android.os.Parcelable; @@ -172,7 +173,7 @@ public class ProfilerInfo implements Parcelable { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (this == o) { return true; } diff --git a/core/java/android/app/PropertyInvalidatedCache.java b/core/java/android/app/PropertyInvalidatedCache.java index 3e3a956c9788..04a12afb8039 100644 --- a/core/java/android/app/PropertyInvalidatedCache.java +++ b/core/java/android/app/PropertyInvalidatedCache.java @@ -206,6 +206,10 @@ public abstract class PropertyInvalidatedCache<Query, Result> { private static final String TAG = "PropertyInvalidatedCache"; private static final boolean DEBUG = false; private static final boolean VERIFY = false; + // If this is true, dumpsys will dump the cache entries along with cache statistics. + // Most of the time this causes dumpsys to fail because the output stream is too + // large. Only set it to true in development images. + private static final boolean DETAILED = false; // Per-Cache performance counters. As some cache instances are declared static, @GuardedBy("mLock") @@ -912,14 +916,13 @@ public abstract class PropertyInvalidatedCache<Query, Result> { " Current Size: %d, Max Size: %d, HW Mark: %d, Overflows: %d", mCache.size(), mMaxEntries, mHighWaterMark, mMissOverflow)); pw.println(String.format(" Enabled: %s", mDisabled ? "false" : "true")); + pw.println(""); Set<Map.Entry<Query, Result>> cacheEntries = mCache.entrySet(); - if (cacheEntries.size() == 0) { - pw.println(""); + if (!DETAILED || cacheEntries.size() == 0) { return; } - pw.println(""); pw.println(" Contents:"); for (Map.Entry<Query, Result> entry : cacheEntries) { String key = Objects.toString(entry.getKey()); diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java index b26784086489..36d5b5eb9fdf 100644 --- a/core/java/android/app/ResourcesManager.java +++ b/core/java/android/app/ResourcesManager.java @@ -127,7 +127,7 @@ public class ResourcesManager { } @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { if (!(obj instanceof ApkKey)) { return false; } diff --git a/core/java/android/app/ResultInfo.java b/core/java/android/app/ResultInfo.java index 979d3dbf36a7..535f69f0e264 100644 --- a/core/java/android/app/ResultInfo.java +++ b/core/java/android/app/ResultInfo.java @@ -16,6 +16,7 @@ package android.app; +import android.annotation.Nullable; import android.compat.annotation.UnsupportedAppUsage; import android.content.Intent; import android.os.Build; @@ -90,7 +91,7 @@ public class ResultInfo implements Parcelable { } @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { if (obj == null || !(obj instanceof ResultInfo)) { return false; } diff --git a/core/java/android/app/RuntimeAppOpAccessMessage.java b/core/java/android/app/RuntimeAppOpAccessMessage.java index a19f815c4298..db3ba4a8cc3f 100644 --- a/core/java/android/app/RuntimeAppOpAccessMessage.java +++ b/core/java/android/app/RuntimeAppOpAccessMessage.java @@ -20,7 +20,6 @@ import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.os.Parcel; import android.os.Parcelable; @@ -32,7 +31,6 @@ import com.android.internal.util.DataClass; * @hide */ @Immutable -@TestApi @SystemApi /*@DataClass(genConstructor = false) @DataClass.Suppress("getOpCode")*/ diff --git a/core/java/android/app/StatusBarManager.java b/core/java/android/app/StatusBarManager.java index 99d21272236d..864db2722d53 100644 --- a/core/java/android/app/StatusBarManager.java +++ b/core/java/android/app/StatusBarManager.java @@ -374,7 +374,6 @@ public class StatusBarManager { * @hide */ @SystemApi - @TestApi @RequiresPermission(android.Manifest.permission.STATUS_BAR) public void setDisabledForSetup(boolean disabled) { try { @@ -423,7 +422,6 @@ public class StatusBarManager { * @hide */ @SystemApi - @TestApi @RequiresPermission(android.Manifest.permission.STATUS_BAR) @NonNull public DisableInfo getDisableInfo() { @@ -456,7 +454,6 @@ public class StatusBarManager { * @hide */ @SystemApi - @TestApi public static final class DisableInfo { private boolean mStatusBarExpansion; @@ -489,7 +486,6 @@ public class StatusBarManager { * @hide */ @SystemApi - @TestApi public boolean isStatusBarExpansionDisabled() { return mStatusBarExpansion; } @@ -505,7 +501,6 @@ public class StatusBarManager { * @hide */ @SystemApi - @TestApi public boolean isNavigateToHomeDisabled() { return mNavigateHome; } @@ -521,7 +516,6 @@ public class StatusBarManager { * @hide */ @SystemApi - @TestApi public boolean isNotificationPeekingDisabled() { return mNotificationPeeking; } @@ -537,7 +531,6 @@ public class StatusBarManager { * @hide */ @SystemApi - @TestApi public boolean isRecentsDisabled() { return mRecents; } @@ -553,7 +546,6 @@ public class StatusBarManager { * @hide */ @SystemApi - @TestApi public boolean isSearchDisabled() { return mSearch; } @@ -611,7 +603,6 @@ public class StatusBarManager { * @hide */ @SystemApi - @TestApi public boolean areAllComponentsEnabled() { return !mStatusBarExpansion && !mNavigateHome && !mNotificationPeeking && !mRecents && !mSearch && !mSystemIcons && !mClock && !mNotificationIcons; diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java index 90401ad53a93..b020c7044a00 100644 --- a/core/java/android/app/SystemServiceRegistry.java +++ b/core/java/android/app/SystemServiceRegistry.java @@ -81,6 +81,7 @@ import android.hardware.SystemSensorManager; import android.hardware.biometrics.BiometricManager; import android.hardware.biometrics.IAuthService; import android.hardware.camera2.CameraManager; +import android.hardware.devicestate.DeviceStateManager; import android.hardware.display.ColorDisplayManager; import android.hardware.display.DisplayManager; import android.hardware.face.FaceManager; @@ -1348,6 +1349,12 @@ public final class SystemServiceRegistry { throws ServiceNotFoundException { return new DreamManager(ctx); }}); + registerService(Context.DEVICE_STATE_SERVICE, DeviceStateManager.class, + new CachedServiceFetcher<DeviceStateManager>() { + @Override + public DeviceStateManager createService(ContextImpl ctx) { + return new DeviceStateManager(); + }}); sInitializing = true; try { @@ -1404,6 +1411,7 @@ public final class SystemServiceRegistry { case Context.CONTENT_CAPTURE_MANAGER_SERVICE: case Context.APP_PREDICTION_SERVICE: case Context.INCREMENTAL_SERVICE: + case Context.ETHERNET_SERVICE: return null; } Slog.wtf(TAG, "Manager wrapper not available: " + name); diff --git a/core/java/android/app/TaskInfo.java b/core/java/android/app/TaskInfo.java index 4718cf1545ce..849f679c9439 100644 --- a/core/java/android/app/TaskInfo.java +++ b/core/java/android/app/TaskInfo.java @@ -24,11 +24,14 @@ import android.content.ComponentName; import android.content.Intent; import android.content.pm.ActivityInfo; import android.content.res.Configuration; +import android.os.IBinder; import android.os.Parcel; import android.os.RemoteException; import android.util.Log; import android.window.WindowContainerToken; +import java.util.ArrayList; + /** * Stores information about a particular Task. */ @@ -177,6 +180,13 @@ public class TaskInfo { */ public boolean isResizeable; + /** + * The launch cookies associated with activities in this task if any. + * @see ActivityOptions#setLaunchCookie(IBinder) + * @hide + */ + public ArrayList<IBinder> launchCookies = new ArrayList<>(); + TaskInfo() { // Do nothing } @@ -213,6 +223,11 @@ public class TaskInfo { return configuration; } + /** @hide */ + public void addLaunchCookie(IBinder cookie) { + launchCookies.add(cookie); + } + /** * Reads the TaskInfo from a parcel. */ @@ -222,9 +237,7 @@ public class TaskInfo { taskId = source.readInt(); displayId = source.readInt(); isRunning = source.readBoolean(); - baseIntent = source.readInt() != 0 - ? Intent.CREATOR.createFromParcel(source) - : null; + baseIntent = source.readTypedObject(Intent.CREATOR); baseActivity = ComponentName.readFromParcel(source); topActivity = ComponentName.readFromParcel(source); origActivity = ComponentName.readFromParcel(source); @@ -233,21 +246,16 @@ public class TaskInfo { numActivities = source.readInt(); lastActiveTime = source.readLong(); - taskDescription = source.readInt() != 0 - ? ActivityManager.TaskDescription.CREATOR.createFromParcel(source) - : null; + taskDescription = source.readTypedObject(ActivityManager.TaskDescription.CREATOR); supportsSplitScreenMultiWindow = source.readBoolean(); resizeMode = source.readInt(); configuration.readFromParcel(source); token = WindowContainerToken.CREATOR.createFromParcel(source); topActivityType = source.readInt(); - pictureInPictureParams = source.readInt() != 0 - ? PictureInPictureParams.CREATOR.createFromParcel(source) - : null; - topActivityInfo = source.readInt() != 0 - ? ActivityInfo.CREATOR.createFromParcel(source) - : null; + pictureInPictureParams = source.readTypedObject(PictureInPictureParams.CREATOR); + topActivityInfo = source.readTypedObject(ActivityInfo.CREATOR); isResizeable = source.readBoolean(); + source.readBinderList(launchCookies); } /** @@ -259,13 +267,8 @@ public class TaskInfo { dest.writeInt(taskId); dest.writeInt(displayId); dest.writeBoolean(isRunning); + dest.writeTypedObject(baseIntent, 0); - if (baseIntent != null) { - dest.writeInt(1); - baseIntent.writeToParcel(dest, 0); - } else { - dest.writeInt(0); - } ComponentName.writeToParcel(baseActivity, dest); ComponentName.writeToParcel(topActivity, dest); ComponentName.writeToParcel(origActivity, dest); @@ -274,30 +277,16 @@ public class TaskInfo { dest.writeInt(numActivities); dest.writeLong(lastActiveTime); - if (taskDescription != null) { - dest.writeInt(1); - taskDescription.writeToParcel(dest, flags); - } else { - dest.writeInt(0); - } + dest.writeTypedObject(taskDescription, flags); dest.writeBoolean(supportsSplitScreenMultiWindow); dest.writeInt(resizeMode); configuration.writeToParcel(dest, flags); token.writeToParcel(dest, flags); dest.writeInt(topActivityType); - if (pictureInPictureParams == null) { - dest.writeInt(0); - } else { - dest.writeInt(1); - pictureInPictureParams.writeToParcel(dest, flags); - } - if (topActivityInfo == null) { - dest.writeInt(0); - } else { - dest.writeInt(1); - topActivityInfo.writeToParcel(dest, flags); - } + dest.writeTypedObject(pictureInPictureParams, flags); + dest.writeTypedObject(topActivityInfo, flags); dest.writeBoolean(isResizeable); + dest.writeBinderList(launchCookies); } @Override @@ -316,6 +305,7 @@ public class TaskInfo { + " token=" + token + " topActivityType=" + topActivityType + " pictureInPictureParams=" + pictureInPictureParams - + " topActivityInfo=" + topActivityInfo; + + " topActivityInfo=" + topActivityInfo + + " launchCookies" + launchCookies; } } diff --git a/core/java/android/app/TaskStackBuilder.java b/core/java/android/app/TaskStackBuilder.java index b99b32748dcd..e23804668d8d 100644 --- a/core/java/android/app/TaskStackBuilder.java +++ b/core/java/android/app/TaskStackBuilder.java @@ -264,6 +264,7 @@ public class TaskStackBuilder { * * @return The obtained PendingIntent */ + @SuppressWarnings("AndroidFrameworkPendingIntentMutability") public PendingIntent getPendingIntent(int requestCode, @PendingIntent.Flags int flags, Bundle options) { if (mIntents.isEmpty()) { @@ -278,6 +279,7 @@ public class TaskStackBuilder { /** * @hide */ + @SuppressWarnings("AndroidFrameworkPendingIntentMutability") public PendingIntent getPendingIntent(int requestCode, int flags, Bundle options, UserHandle user) { if (mIntents.isEmpty()) { diff --git a/core/java/android/app/UiModeManager.java b/core/java/android/app/UiModeManager.java index 06d1b74abc86..e2fc5dbf10e2 100644 --- a/core/java/android/app/UiModeManager.java +++ b/core/java/android/app/UiModeManager.java @@ -314,7 +314,6 @@ public class UiModeManager { * @hide */ @SystemApi - @TestApi @RequiresPermission(android.Manifest.permission.ENTER_CAR_MODE_PRIORITIZED) public void enableCarMode(@IntRange(from = 0) int priority, @EnableCarMode int flags) { if (mService != null) { diff --git a/core/java/android/app/WallpaperColors.java b/core/java/android/app/WallpaperColors.java index 7624f356eb6a..2d203f57a66f 100644 --- a/core/java/android/app/WallpaperColors.java +++ b/core/java/android/app/WallpaperColors.java @@ -337,7 +337,7 @@ public final class WallpaperColors implements Parcelable { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (o == null || getClass() != o.getClass()) { return false; } diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java index 0a80ccc13487..54f3209e17be 100644 --- a/core/java/android/app/WallpaperManager.java +++ b/core/java/android/app/WallpaperManager.java @@ -1766,7 +1766,6 @@ public class WallpaperManager { * * @hide */ - @TestApi @SystemApi @RequiresPermission(android.Manifest.permission.SET_WALLPAPER_COMPONENT) public boolean setWallpaperComponent(ComponentName name) { diff --git a/core/java/android/app/WindowConfiguration.java b/core/java/android/app/WindowConfiguration.java index 79f05a3caa93..4ae1670e9041 100644 --- a/core/java/android/app/WindowConfiguration.java +++ b/core/java/android/app/WindowConfiguration.java @@ -639,7 +639,7 @@ public class WindowConfiguration implements Parcelable, Comparable<WindowConfigu /** @hide */ @Override - public boolean equals(Object that) { + public boolean equals(@Nullable Object that) { if (that == null) return false; if (that == this) return true; if (!(that instanceof WindowConfiguration)) { @@ -852,15 +852,6 @@ public class WindowConfiguration implements Parcelable, Comparable<WindowConfigu } /** - * Returns true if this container may be scaled without resizing, and windows within may need - * to be configured as such. - * @hide - */ - public boolean windowsAreScaleable() { - return mWindowingMode == WINDOWING_MODE_PINNED; - } - - /** * Returns true if windows in this container should be given move animations by default. * @hide */ diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index b74e18b099ce..ad902a028f13 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -1465,7 +1465,7 @@ public class DevicePolicyManager { * @see #createAdminSupportIntent(String) * @hide */ - @TestApi @SystemApi + @SystemApi public static final String EXTRA_RESTRICTION = "android.app.extra.RESTRICTION"; /** @@ -2688,13 +2688,11 @@ public class DevicePolicyManager { * </ul> */ @SystemApi - @TestApi public static final String ACCOUNT_FEATURE_DEVICE_OR_PROFILE_OWNER_ALLOWED = "android.account.DEVICE_OR_PROFILE_OWNER_ALLOWED"; /** @hide See {@link #ACCOUNT_FEATURE_DEVICE_OR_PROFILE_OWNER_ALLOWED} */ @SystemApi - @TestApi public static final String ACCOUNT_FEATURE_DEVICE_OR_PROFILE_OWNER_DISALLOWED = "android.account.DEVICE_OR_PROFILE_OWNER_DISALLOWED"; @@ -6671,7 +6669,6 @@ public class DevicePolicyManager { * @hide */ @SystemApi - @TestApi @SuppressLint("Doclava125") public boolean isDeviceManaged() { try { @@ -10381,7 +10378,6 @@ public class DevicePolicyManager { * @hide */ @SystemApi - @TestApi @SuppressLint("Doclava125") public @Nullable CharSequence getDeviceOwnerOrganizationName() { try { diff --git a/core/java/android/app/admin/PasswordMetrics.java b/core/java/android/app/admin/PasswordMetrics.java index 39e1f0dc2d2c..20a60bbe0974 100644 --- a/core/java/android/app/admin/PasswordMetrics.java +++ b/core/java/android/app/admin/PasswordMetrics.java @@ -44,6 +44,7 @@ import static com.android.internal.widget.PasswordValidationError.WEAK_CREDENTIA import android.annotation.IntDef; import android.annotation.NonNull; +import android.annotation.Nullable; import android.app.admin.DevicePolicyManager.PasswordComplexity; import android.os.Parcel; import android.os.Parcelable; @@ -705,7 +706,7 @@ public final class PasswordMetrics implements Parcelable { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; final PasswordMetrics that = (PasswordMetrics) o; diff --git a/core/java/android/app/admin/SecurityLog.java b/core/java/android/app/admin/SecurityLog.java index fb7f573d5535..5a4ab48f3823 100644 --- a/core/java/android/app/admin/SecurityLog.java +++ b/core/java/android/app/admin/SecurityLog.java @@ -17,6 +17,7 @@ package android.app.admin; import android.annotation.IntDef; +import android.annotation.Nullable; import android.annotation.TestApi; import android.compat.annotation.UnsupportedAppUsage; import android.content.ComponentName; @@ -745,7 +746,7 @@ public class SecurityLog { * @hide */ @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; SecurityEvent other = (SecurityEvent) o; diff --git a/core/java/android/app/admin/SystemUpdateInfo.java b/core/java/android/app/admin/SystemUpdateInfo.java index 4019290c883b..53b238633fec 100644 --- a/core/java/android/app/admin/SystemUpdateInfo.java +++ b/core/java/android/app/admin/SystemUpdateInfo.java @@ -179,7 +179,7 @@ public final class SystemUpdateInfo implements Parcelable { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; SystemUpdateInfo that = (SystemUpdateInfo) o; diff --git a/core/java/android/app/assist/AssistStructure.java b/core/java/android/app/assist/AssistStructure.java index b5234f8e2e17..c15504cc0843 100644 --- a/core/java/android/app/assist/AssistStructure.java +++ b/core/java/android/app/assist/AssistStructure.java @@ -3,7 +3,6 @@ package android.app.assist; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.app.Activity; import android.content.ComponentName; import android.content.Context; @@ -722,7 +721,6 @@ public class AssistStructure implements Parcelable { // COntent Capture. /** @hide */ @SystemApi - @TestApi public ViewNode() { } diff --git a/core/java/android/app/backup/BackupAgent.java b/core/java/android/app/backup/BackupAgent.java index 065127138ecc..7e232acbdcdd 100644 --- a/core/java/android/app/backup/BackupAgent.java +++ b/core/java/android/app/backup/BackupAgent.java @@ -1358,7 +1358,7 @@ public abstract class BackupAgent extends ContextWrapper { /** @hide */ @Override - public boolean equals(Object object) { + public boolean equals(@Nullable Object object) { if (this == object) { return true; } diff --git a/core/java/android/app/backup/BackupManager.java b/core/java/android/app/backup/BackupManager.java index 9b67587c4dcd..05313592faa5 100644 --- a/core/java/android/app/backup/BackupManager.java +++ b/core/java/android/app/backup/BackupManager.java @@ -21,7 +21,6 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.compat.annotation.UnsupportedAppUsage; import android.content.ComponentName; import android.content.Context; @@ -919,7 +918,6 @@ public class BackupManager { * @hide */ @SystemApi - @TestApi @RequiresPermission(android.Manifest.permission.BACKUP) public Intent getConfigurationIntent(String transportName) { checkServiceBinder(); @@ -941,7 +939,6 @@ public class BackupManager { * @hide */ @SystemApi - @TestApi @RequiresPermission(android.Manifest.permission.BACKUP) public String getDestinationString(String transportName) { checkServiceBinder(); @@ -963,7 +960,6 @@ public class BackupManager { * @hide */ @SystemApi - @TestApi @RequiresPermission(android.Manifest.permission.BACKUP) public Intent getDataManagementIntent(String transportName) { checkServiceBinder(); @@ -989,7 +985,6 @@ public class BackupManager { */ @Deprecated @SystemApi - @TestApi @RequiresPermission(android.Manifest.permission.BACKUP) @Nullable public String getDataManagementLabel(@NonNull String transportName) { @@ -1006,7 +1001,6 @@ public class BackupManager { * @hide */ @SystemApi - @TestApi @RequiresPermission(android.Manifest.permission.BACKUP) @Nullable public CharSequence getDataManagementIntentLabel(@NonNull String transportName) { diff --git a/core/java/android/app/compat/ChangeIdStateQuery.java b/core/java/android/app/compat/ChangeIdStateQuery.java index 3c245b16f6d6..91765f78a2b0 100644 --- a/core/java/android/app/compat/ChangeIdStateQuery.java +++ b/core/java/android/app/compat/ChangeIdStateQuery.java @@ -18,6 +18,7 @@ package android.app.compat; import android.annotation.IntDef; import android.annotation.NonNull; +import android.annotation.Nullable; import com.android.internal.annotations.Immutable; @@ -25,7 +26,6 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.Objects; - /** * A key type for caching calls to {@link com.android.internal.compat.IPlatformCompat} * @@ -68,7 +68,7 @@ final class ChangeIdStateQuery { } @Override - public boolean equals(Object other) { + public boolean equals(@Nullable Object other) { if (this == other) { return true; } diff --git a/core/java/android/app/prediction/AppPredictionContext.java b/core/java/android/app/prediction/AppPredictionContext.java index d14238bb2672..99fa869cee93 100644 --- a/core/java/android/app/prediction/AppPredictionContext.java +++ b/core/java/android/app/prediction/AppPredictionContext.java @@ -19,7 +19,6 @@ import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.content.Context; import android.os.Bundle; import android.os.Parcel; @@ -32,7 +31,6 @@ import android.os.Parcelable; * @hide */ @SystemApi -@TestApi public final class AppPredictionContext implements Parcelable { private final int mPredictedTargetCount; @@ -129,7 +127,6 @@ public final class AppPredictionContext implements Parcelable { * @hide */ @SystemApi - @TestApi public static final class Builder { @NonNull @@ -147,7 +144,6 @@ public final class AppPredictionContext implements Parcelable { * @hide */ @SystemApi - @TestApi public Builder(@NonNull Context context) { mPackageName = context.getPackageName(); } diff --git a/core/java/android/app/prediction/AppPredictionManager.java b/core/java/android/app/prediction/AppPredictionManager.java index ca22721622b7..5da7aa97ea0f 100644 --- a/core/java/android/app/prediction/AppPredictionManager.java +++ b/core/java/android/app/prediction/AppPredictionManager.java @@ -17,7 +17,6 @@ package android.app.prediction; import android.annotation.NonNull; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.content.Context; import java.util.Objects; @@ -28,7 +27,6 @@ import java.util.Objects; * @hide */ @SystemApi -@TestApi public final class AppPredictionManager { private final Context mContext; diff --git a/core/java/android/app/prediction/AppPredictionSessionId.java b/core/java/android/app/prediction/AppPredictionSessionId.java index 876bafdfb7d1..6277a7de258f 100644 --- a/core/java/android/app/prediction/AppPredictionSessionId.java +++ b/core/java/android/app/prediction/AppPredictionSessionId.java @@ -18,7 +18,6 @@ package android.app.prediction; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.os.Parcel; import android.os.Parcelable; @@ -30,7 +29,6 @@ import java.util.Objects; * @hide */ @SystemApi -@TestApi public final class AppPredictionSessionId implements Parcelable { private final String mId; diff --git a/core/java/android/app/prediction/AppPredictor.java b/core/java/android/app/prediction/AppPredictor.java index fa135b10ae1f..fd1b9e3bede2 100644 --- a/core/java/android/app/prediction/AppPredictor.java +++ b/core/java/android/app/prediction/AppPredictor.java @@ -70,7 +70,6 @@ import java.util.function.Consumer; * @hide */ @SystemApi -@TestApi public final class AppPredictor { private static final String TAG = AppPredictor.class.getSimpleName(); diff --git a/core/java/android/app/prediction/AppTarget.java b/core/java/android/app/prediction/AppTarget.java index 14e32b8397a8..fef9e7020097 100644 --- a/core/java/android/app/prediction/AppTarget.java +++ b/core/java/android/app/prediction/AppTarget.java @@ -19,7 +19,6 @@ import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.content.pm.ShortcutInfo; import android.os.Parcel; import android.os.Parcelable; @@ -33,7 +32,6 @@ import java.util.Objects; * @hide */ @SystemApi -@TestApi public final class AppTarget implements Parcelable { private final AppTargetId mId; @@ -190,7 +188,6 @@ public final class AppTarget implements Parcelable { * @hide */ @SystemApi - @TestApi public static final class Builder { @NonNull @@ -221,7 +218,6 @@ public final class AppTarget implements Parcelable { * @hide */ @SystemApi - @TestApi public Builder(@NonNull AppTargetId id, @NonNull String packageName, @NonNull UserHandle user) { mId = Objects.requireNonNull(id); @@ -235,7 +231,6 @@ public final class AppTarget implements Parcelable { * @hide */ @SystemApi - @TestApi public Builder(@NonNull AppTargetId id, @NonNull ShortcutInfo info) { mId = Objects.requireNonNull(id); mShortcutInfo = Objects.requireNonNull(info); diff --git a/core/java/android/app/prediction/AppTargetEvent.java b/core/java/android/app/prediction/AppTargetEvent.java index f519145c4aa8..963e750e4fd1 100644 --- a/core/java/android/app/prediction/AppTargetEvent.java +++ b/core/java/android/app/prediction/AppTargetEvent.java @@ -19,7 +19,6 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.os.Parcel; import android.os.Parcelable; @@ -32,7 +31,6 @@ import java.lang.annotation.RetentionPolicy; * @hide */ @SystemApi -@TestApi public final class AppTargetEvent implements Parcelable { /** @@ -141,7 +139,6 @@ public final class AppTargetEvent implements Parcelable { * @hide */ @SystemApi - @TestApi public static final class Builder { private AppTarget mTarget; private String mLocation; diff --git a/core/java/android/app/prediction/AppTargetId.java b/core/java/android/app/prediction/AppTargetId.java index 052fdc11ef21..048e12c5c48e 100644 --- a/core/java/android/app/prediction/AppTargetId.java +++ b/core/java/android/app/prediction/AppTargetId.java @@ -18,7 +18,6 @@ package android.app.prediction; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.os.Parcel; import android.os.Parcelable; @@ -28,7 +27,6 @@ import android.os.Parcelable; * @hide */ @SystemApi -@TestApi public final class AppTargetId implements Parcelable { @NonNull @@ -40,7 +38,6 @@ public final class AppTargetId implements Parcelable { * @hide */ @SystemApi - @TestApi public AppTargetId(@NonNull String id) { mId = id; } diff --git a/core/java/android/app/role/OnRoleHoldersChangedListener.java b/core/java/android/app/role/OnRoleHoldersChangedListener.java index d6f76794da20..5958debc86dd 100644 --- a/core/java/android/app/role/OnRoleHoldersChangedListener.java +++ b/core/java/android/app/role/OnRoleHoldersChangedListener.java @@ -18,7 +18,6 @@ package android.app.role; import android.annotation.NonNull; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.os.UserHandle; /** @@ -27,7 +26,6 @@ import android.os.UserHandle; * @hide */ @SystemApi -@TestApi public interface OnRoleHoldersChangedListener { /** diff --git a/core/java/android/app/role/RoleManager.java b/core/java/android/app/role/RoleManager.java index 82159235ae28..408ce0f2ab1a 100644 --- a/core/java/android/app/role/RoleManager.java +++ b/core/java/android/app/role/RoleManager.java @@ -144,7 +144,6 @@ public final class RoleManager { * @hide */ @SystemApi - @TestApi public static final int MANAGE_HOLDERS_FLAG_DONT_KILL_APP = 1; /** @@ -255,7 +254,6 @@ public final class RoleManager { @NonNull @RequiresPermission(Manifest.permission.MANAGE_ROLE_HOLDERS) @SystemApi - @TestApi public List<String> getRoleHolders(@NonNull String roleName) { return getRoleHoldersAsUser(roleName, Process.myUserHandle()); } @@ -281,7 +279,6 @@ public final class RoleManager { @NonNull @RequiresPermission(Manifest.permission.MANAGE_ROLE_HOLDERS) @SystemApi - @TestApi public List<String> getRoleHoldersAsUser(@NonNull String roleName, @NonNull UserHandle user) { Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty"); Objects.requireNonNull(user, "user cannot be null"); @@ -315,7 +312,6 @@ public final class RoleManager { */ @RequiresPermission(Manifest.permission.MANAGE_ROLE_HOLDERS) @SystemApi - @TestApi public void addRoleHolderAsUser(@NonNull String roleName, @NonNull String packageName, @ManageHoldersFlags int flags, @NonNull UserHandle user, @CallbackExecutor @NonNull Executor executor, @NonNull Consumer<Boolean> callback) { @@ -354,7 +350,6 @@ public final class RoleManager { */ @RequiresPermission(Manifest.permission.MANAGE_ROLE_HOLDERS) @SystemApi - @TestApi public void removeRoleHolderAsUser(@NonNull String roleName, @NonNull String packageName, @ManageHoldersFlags int flags, @NonNull UserHandle user, @CallbackExecutor @NonNull Executor executor, @NonNull Consumer<Boolean> callback) { @@ -392,7 +387,6 @@ public final class RoleManager { */ @RequiresPermission(Manifest.permission.MANAGE_ROLE_HOLDERS) @SystemApi - @TestApi public void clearRoleHoldersAsUser(@NonNull String roleName, @ManageHoldersFlags int flags, @NonNull UserHandle user, @CallbackExecutor @NonNull Executor executor, @NonNull Consumer<Boolean> callback) { @@ -439,7 +433,6 @@ public final class RoleManager { */ @RequiresPermission(Manifest.permission.OBSERVE_ROLE_HOLDERS) @SystemApi - @TestApi public void addOnRoleHoldersChangedListenerAsUser(@CallbackExecutor @NonNull Executor executor, @NonNull OnRoleHoldersChangedListener listener, @NonNull UserHandle user) { Objects.requireNonNull(executor, "executor cannot be null"); @@ -485,7 +478,6 @@ public final class RoleManager { */ @RequiresPermission(Manifest.permission.OBSERVE_ROLE_HOLDERS) @SystemApi - @TestApi public void removeOnRoleHoldersChangedListenerAsUser( @NonNull OnRoleHoldersChangedListener listener, @NonNull UserHandle user) { Objects.requireNonNull(listener, "listener cannot be null"); @@ -527,7 +519,6 @@ public final class RoleManager { */ @RequiresPermission(PERMISSION_MANAGE_ROLES_FROM_CONTROLLER) @SystemApi - @TestApi public void setRoleNamesFromController(@NonNull List<String> roleNames) { Objects.requireNonNull(roleNames, "roleNames cannot be null"); try { @@ -558,7 +549,6 @@ public final class RoleManager { */ @RequiresPermission(PERMISSION_MANAGE_ROLES_FROM_CONTROLLER) @SystemApi - @TestApi public boolean addRoleHolderFromController(@NonNull String roleName, @NonNull String packageName) { Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty"); @@ -591,7 +581,6 @@ public final class RoleManager { */ @RequiresPermission(PERMISSION_MANAGE_ROLES_FROM_CONTROLLER) @SystemApi - @TestApi public boolean removeRoleHolderFromController(@NonNull String roleName, @NonNull String packageName) { Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty"); @@ -614,7 +603,6 @@ public final class RoleManager { @NonNull @RequiresPermission(PERMISSION_MANAGE_ROLES_FROM_CONTROLLER) @SystemApi - @TestApi public List<String> getHeldRolesFromController(@NonNull String packageName) { Preconditions.checkStringNotEmpty(packageName, "packageName cannot be null or empty"); try { diff --git a/core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java b/core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java index 8a4bee98ca87..d5585db08994 100644 --- a/core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java +++ b/core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java @@ -20,6 +20,7 @@ import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER; import static android.view.Display.INVALID_DISPLAY; import android.annotation.NonNull; +import android.annotation.Nullable; import android.app.ActivityThread.ActivityClientRecord; import android.app.ClientTransactionHandler; import android.content.res.Configuration; @@ -107,7 +108,7 @@ public class ActivityConfigurationChangeItem extends ActivityTransactionItem { }; @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (this == o) { return true; } diff --git a/core/java/android/app/servertransaction/ActivityRelaunchItem.java b/core/java/android/app/servertransaction/ActivityRelaunchItem.java index 87ea3f8db39c..b3e34b366082 100644 --- a/core/java/android/app/servertransaction/ActivityRelaunchItem.java +++ b/core/java/android/app/servertransaction/ActivityRelaunchItem.java @@ -19,6 +19,7 @@ package android.app.servertransaction; import static android.app.ActivityThread.DEBUG_ORDER; import android.annotation.NonNull; +import android.annotation.Nullable; import android.app.ActivityThread.ActivityClientRecord; import android.app.ClientTransactionHandler; import android.app.ResultInfo; @@ -144,7 +145,7 @@ public class ActivityRelaunchItem extends ActivityTransactionItem { }; @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (this == o) { return true; } diff --git a/core/java/android/app/servertransaction/ActivityResultItem.java b/core/java/android/app/servertransaction/ActivityResultItem.java index 47096a8257e9..8320f49f132f 100644 --- a/core/java/android/app/servertransaction/ActivityResultItem.java +++ b/core/java/android/app/servertransaction/ActivityResultItem.java @@ -19,6 +19,7 @@ package android.app.servertransaction; import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER; import android.annotation.NonNull; +import android.annotation.Nullable; import android.app.ActivityThread.ActivityClientRecord; import android.app.ClientTransactionHandler; import android.app.ResultInfo; @@ -101,7 +102,7 @@ public class ActivityResultItem extends ActivityTransactionItem { }; @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (this == o) { return true; } diff --git a/core/java/android/app/servertransaction/ClientTransaction.java b/core/java/android/app/servertransaction/ClientTransaction.java index 3d044376ee89..90890d58aaff 100644 --- a/core/java/android/app/servertransaction/ClientTransaction.java +++ b/core/java/android/app/servertransaction/ClientTransaction.java @@ -222,7 +222,7 @@ public class ClientTransaction implements Parcelable, ObjectPoolItem { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (this == o) { return true; } diff --git a/core/java/android/app/servertransaction/ConfigurationChangeItem.java b/core/java/android/app/servertransaction/ConfigurationChangeItem.java index 0f244d02f7a1..2acff497b293 100644 --- a/core/java/android/app/servertransaction/ConfigurationChangeItem.java +++ b/core/java/android/app/servertransaction/ConfigurationChangeItem.java @@ -16,6 +16,7 @@ package android.app.servertransaction; +import android.annotation.Nullable; import android.app.ClientTransactionHandler; import android.content.res.Configuration; import android.os.IBinder; @@ -90,7 +91,7 @@ public class ConfigurationChangeItem extends ClientTransactionItem { }; @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (this == o) { return true; } diff --git a/core/java/android/app/servertransaction/DestroyActivityItem.java b/core/java/android/app/servertransaction/DestroyActivityItem.java index 1611369497e9..a074286a4ecd 100644 --- a/core/java/android/app/servertransaction/DestroyActivityItem.java +++ b/core/java/android/app/servertransaction/DestroyActivityItem.java @@ -19,6 +19,7 @@ package android.app.servertransaction; import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER; import android.annotation.NonNull; +import android.annotation.Nullable; import android.app.ActivityThread.ActivityClientRecord; import android.app.ClientTransactionHandler; import android.os.IBinder; @@ -106,7 +107,7 @@ public class DestroyActivityItem extends ActivityLifecycleItem { }; @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (this == o) { return true; } diff --git a/core/java/android/app/servertransaction/EnterPipRequestedItem.java b/core/java/android/app/servertransaction/EnterPipRequestedItem.java index b7e81a56afad..7dcae654e1d0 100644 --- a/core/java/android/app/servertransaction/EnterPipRequestedItem.java +++ b/core/java/android/app/servertransaction/EnterPipRequestedItem.java @@ -16,6 +16,7 @@ package android.app.servertransaction; +import android.annotation.Nullable; import android.app.ActivityThread.ActivityClientRecord; import android.app.ClientTransactionHandler; import android.os.Parcel; @@ -67,7 +68,7 @@ public final class EnterPipRequestedItem extends ActivityTransactionItem { }; @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { return this == o; } diff --git a/core/java/android/app/servertransaction/FixedRotationAdjustmentsItem.java b/core/java/android/app/servertransaction/FixedRotationAdjustmentsItem.java index 6183d5f28fdb..4c063333fad9 100644 --- a/core/java/android/app/servertransaction/FixedRotationAdjustmentsItem.java +++ b/core/java/android/app/servertransaction/FixedRotationAdjustmentsItem.java @@ -16,6 +16,7 @@ package android.app.servertransaction; +import android.annotation.Nullable; import android.app.ClientTransactionHandler; import android.os.IBinder; import android.os.Parcel; @@ -74,7 +75,7 @@ public class FixedRotationAdjustmentsItem extends ClientTransactionItem { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (this == o) { return true; } diff --git a/core/java/android/app/servertransaction/LaunchActivityItem.java b/core/java/android/app/servertransaction/LaunchActivityItem.java index 77457af77340..7f08bfbef289 100644 --- a/core/java/android/app/servertransaction/LaunchActivityItem.java +++ b/core/java/android/app/servertransaction/LaunchActivityItem.java @@ -19,6 +19,7 @@ package android.app.servertransaction; import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER; import android.annotation.NonNull; +import android.annotation.Nullable; import android.app.ActivityThread.ActivityClientRecord; import android.app.ClientTransactionHandler; import android.app.ProfilerInfo; @@ -176,7 +177,7 @@ public class LaunchActivityItem extends ClientTransactionItem { }; @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (this == o) { return true; } diff --git a/core/java/android/app/servertransaction/MoveToDisplayItem.java b/core/java/android/app/servertransaction/MoveToDisplayItem.java index 32de53f189b0..944367e304e6 100644 --- a/core/java/android/app/servertransaction/MoveToDisplayItem.java +++ b/core/java/android/app/servertransaction/MoveToDisplayItem.java @@ -19,6 +19,7 @@ package android.app.servertransaction; import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER; import android.annotation.NonNull; +import android.annotation.Nullable; import android.app.ActivityThread.ActivityClientRecord; import android.app.ClientTransactionHandler; import android.content.res.Configuration; @@ -110,7 +111,7 @@ public class MoveToDisplayItem extends ActivityTransactionItem { }; @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (this == o) { return true; } diff --git a/core/java/android/app/servertransaction/NewIntentItem.java b/core/java/android/app/servertransaction/NewIntentItem.java index b4e2a7bfa10f..ac57f2bf80e0 100644 --- a/core/java/android/app/servertransaction/NewIntentItem.java +++ b/core/java/android/app/servertransaction/NewIntentItem.java @@ -20,6 +20,7 @@ import static android.app.servertransaction.ActivityLifecycleItem.ON_RESUME; import static android.app.servertransaction.ActivityLifecycleItem.UNDEFINED; import android.annotation.NonNull; +import android.annotation.Nullable; import android.app.ActivityThread.ActivityClientRecord; import android.app.ClientTransactionHandler; import android.compat.annotation.UnsupportedAppUsage; @@ -107,7 +108,7 @@ public class NewIntentItem extends ActivityTransactionItem { }; @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (this == o) { return true; } diff --git a/core/java/android/app/servertransaction/PauseActivityItem.java b/core/java/android/app/servertransaction/PauseActivityItem.java index cb154e9585e6..f7c645e7cb38 100644 --- a/core/java/android/app/servertransaction/PauseActivityItem.java +++ b/core/java/android/app/servertransaction/PauseActivityItem.java @@ -19,6 +19,7 @@ package android.app.servertransaction; import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER; import android.annotation.NonNull; +import android.annotation.Nullable; import android.app.ActivityTaskManager; import android.app.ActivityThread.ActivityClientRecord; import android.app.ClientTransactionHandler; @@ -143,7 +144,7 @@ public class PauseActivityItem extends ActivityLifecycleItem { }; @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (this == o) { return true; } diff --git a/core/java/android/app/servertransaction/ResumeActivityItem.java b/core/java/android/app/servertransaction/ResumeActivityItem.java index d2a156c37c90..b4523f581ba8 100644 --- a/core/java/android/app/servertransaction/ResumeActivityItem.java +++ b/core/java/android/app/servertransaction/ResumeActivityItem.java @@ -19,6 +19,7 @@ package android.app.servertransaction; import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER; import android.annotation.NonNull; +import android.annotation.Nullable; import android.app.ActivityManager; import android.app.ActivityTaskManager; import android.app.ActivityThread.ActivityClientRecord; @@ -142,7 +143,7 @@ public class ResumeActivityItem extends ActivityLifecycleItem { }; @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (this == o) { return true; } diff --git a/core/java/android/app/servertransaction/StartActivityItem.java b/core/java/android/app/servertransaction/StartActivityItem.java index ae0bd24218fb..483f9de58a1c 100644 --- a/core/java/android/app/servertransaction/StartActivityItem.java +++ b/core/java/android/app/servertransaction/StartActivityItem.java @@ -19,6 +19,7 @@ package android.app.servertransaction; import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER; import android.annotation.NonNull; +import android.annotation.Nullable; import android.app.ActivityThread.ActivityClientRecord; import android.app.ClientTransactionHandler; import android.os.Parcel; @@ -92,7 +93,7 @@ public class StartActivityItem extends ActivityLifecycleItem { }; @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (this == o) { return true; } diff --git a/core/java/android/app/servertransaction/StopActivityItem.java b/core/java/android/app/servertransaction/StopActivityItem.java index 7708104da16a..7e9116dfdfc7 100644 --- a/core/java/android/app/servertransaction/StopActivityItem.java +++ b/core/java/android/app/servertransaction/StopActivityItem.java @@ -19,6 +19,7 @@ package android.app.servertransaction; import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER; import android.annotation.NonNull; +import android.annotation.Nullable; import android.app.ActivityThread.ActivityClientRecord; import android.app.ClientTransactionHandler; import android.os.IBinder; @@ -107,7 +108,7 @@ public class StopActivityItem extends ActivityLifecycleItem { }; @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (this == o) { return true; } diff --git a/core/java/android/app/servertransaction/TopResumedActivityChangeItem.java b/core/java/android/app/servertransaction/TopResumedActivityChangeItem.java index 345c1dd336ab..2b0c1b9869e0 100644 --- a/core/java/android/app/servertransaction/TopResumedActivityChangeItem.java +++ b/core/java/android/app/servertransaction/TopResumedActivityChangeItem.java @@ -18,6 +18,7 @@ package android.app.servertransaction; import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER; import android.annotation.NonNull; +import android.annotation.Nullable; import android.app.ActivityTaskManager; import android.app.ActivityThread.ActivityClientRecord; import android.app.ClientTransactionHandler; @@ -111,7 +112,7 @@ public class TopResumedActivityChangeItem extends ActivityTransactionItem { }; @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (this == o) { return true; } diff --git a/core/java/android/app/slice/SliceProvider.java b/core/java/android/app/slice/SliceProvider.java index cef6ab094177..0589f4a3b2bb 100644 --- a/core/java/android/app/slice/SliceProvider.java +++ b/core/java/android/app/slice/SliceProvider.java @@ -519,7 +519,7 @@ public abstract class SliceProvider extends ContentProvider { intent.setData(sliceUri.buildUpon().appendQueryParameter("package", callingPackage) .build()); - return PendingIntent.getActivity(context, 0, intent, 0); + return PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_IMMUTABLE); } /** diff --git a/core/java/android/app/slice/SliceSpec.java b/core/java/android/app/slice/SliceSpec.java index b1080e07e450..a33234981059 100644 --- a/core/java/android/app/slice/SliceSpec.java +++ b/core/java/android/app/slice/SliceSpec.java @@ -17,6 +17,7 @@ package android.app.slice; import android.annotation.NonNull; +import android.annotation.Nullable; import android.net.Uri; import android.os.Parcel; import android.os.Parcelable; @@ -97,7 +98,7 @@ public final class SliceSpec implements Parcelable { } @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { if (!(obj instanceof SliceSpec)) return false; SliceSpec other = (SliceSpec) obj; return mType.equals(other.mType) && mRevision == other.mRevision; diff --git a/core/java/android/app/time/TimeZoneCapabilities.java b/core/java/android/app/time/TimeZoneCapabilities.java index df89c285d5bd..7cc67b310f31 100644 --- a/core/java/android/app/time/TimeZoneCapabilities.java +++ b/core/java/android/app/time/TimeZoneCapabilities.java @@ -205,7 +205,7 @@ public final class TimeZoneCapabilities implements Parcelable { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (this == o) { return true; } diff --git a/core/java/android/app/time/TimeZoneCapabilitiesAndConfig.java b/core/java/android/app/time/TimeZoneCapabilitiesAndConfig.java index b339e53b8be3..f9a0c74312fc 100644 --- a/core/java/android/app/time/TimeZoneCapabilitiesAndConfig.java +++ b/core/java/android/app/time/TimeZoneCapabilitiesAndConfig.java @@ -17,6 +17,7 @@ package android.app.time; import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.SystemApi; import android.os.Parcel; import android.os.Parcelable; @@ -93,7 +94,7 @@ public final class TimeZoneCapabilitiesAndConfig implements Parcelable { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (this == o) { return true; } diff --git a/core/java/android/app/time/TimeZoneConfiguration.java b/core/java/android/app/time/TimeZoneConfiguration.java index c0a0c21b546d..7403c129f4dc 100644 --- a/core/java/android/app/time/TimeZoneConfiguration.java +++ b/core/java/android/app/time/TimeZoneConfiguration.java @@ -17,6 +17,7 @@ package android.app.time; import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.StringDef; import android.annotation.SystemApi; import android.os.Bundle; @@ -155,7 +156,7 @@ public final class TimeZoneConfiguration implements Parcelable { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (this == o) { return true; } diff --git a/core/java/android/app/timedetector/ManualTimeSuggestion.java b/core/java/android/app/timedetector/ManualTimeSuggestion.java index da51ce2a6aff..299e9518e329 100644 --- a/core/java/android/app/timedetector/ManualTimeSuggestion.java +++ b/core/java/android/app/timedetector/ManualTimeSuggestion.java @@ -109,7 +109,7 @@ public final class ManualTimeSuggestion implements Parcelable { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (this == o) { return true; } diff --git a/core/java/android/app/timedetector/NetworkTimeSuggestion.java b/core/java/android/app/timedetector/NetworkTimeSuggestion.java index 89fd6f31e042..a5259c27ec42 100644 --- a/core/java/android/app/timedetector/NetworkTimeSuggestion.java +++ b/core/java/android/app/timedetector/NetworkTimeSuggestion.java @@ -109,7 +109,7 @@ public final class NetworkTimeSuggestion implements Parcelable { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (this == o) { return true; } diff --git a/core/java/android/app/timedetector/TelephonyTimeSuggestion.java b/core/java/android/app/timedetector/TelephonyTimeSuggestion.java index c0e8957727cf..6c3a304ed3a7 100644 --- a/core/java/android/app/timedetector/TelephonyTimeSuggestion.java +++ b/core/java/android/app/timedetector/TelephonyTimeSuggestion.java @@ -154,7 +154,7 @@ public final class TelephonyTimeSuggestion implements Parcelable { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (this == o) { return true; } diff --git a/core/java/android/app/timezone/DistroFormatVersion.java b/core/java/android/app/timezone/DistroFormatVersion.java index 04e31421d1d7..13ecaf51052c 100644 --- a/core/java/android/app/timezone/DistroFormatVersion.java +++ b/core/java/android/app/timezone/DistroFormatVersion.java @@ -16,6 +16,7 @@ package android.app.timezone; +import android.annotation.Nullable; import android.os.Parcel; import android.os.Parcelable; @@ -86,7 +87,7 @@ public final class DistroFormatVersion implements Parcelable { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (this == o) { return true; } diff --git a/core/java/android/app/timezone/DistroRulesVersion.java b/core/java/android/app/timezone/DistroRulesVersion.java index 3fae1612fa88..54937b8e6c3a 100644 --- a/core/java/android/app/timezone/DistroRulesVersion.java +++ b/core/java/android/app/timezone/DistroRulesVersion.java @@ -19,6 +19,7 @@ package android.app.timezone; import static android.app.timezone.Utils.validateRulesVersion; import static android.app.timezone.Utils.validateVersion; +import android.annotation.Nullable; import android.os.Parcel; import android.os.Parcelable; @@ -94,7 +95,7 @@ public final class DistroRulesVersion implements Parcelable { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (this == o) { return true; } diff --git a/core/java/android/app/timezone/RulesState.java b/core/java/android/app/timezone/RulesState.java index d35967df3416..ee88ec54d08f 100644 --- a/core/java/android/app/timezone/RulesState.java +++ b/core/java/android/app/timezone/RulesState.java @@ -223,7 +223,7 @@ public final class RulesState implements Parcelable { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (this == o) { return true; } diff --git a/core/java/android/app/timezonedetector/ManualTimeZoneSuggestion.java b/core/java/android/app/timezonedetector/ManualTimeZoneSuggestion.java index 002c66361233..01a60b1fa025 100644 --- a/core/java/android/app/timezonedetector/ManualTimeZoneSuggestion.java +++ b/core/java/android/app/timezonedetector/ManualTimeZoneSuggestion.java @@ -105,7 +105,7 @@ public final class ManualTimeZoneSuggestion implements Parcelable { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (this == o) { return true; } diff --git a/core/java/android/app/timezonedetector/TelephonyTimeZoneSuggestion.java b/core/java/android/app/timezonedetector/TelephonyTimeZoneSuggestion.java index 430462b07c7a..eb6750f06d25 100644 --- a/core/java/android/app/timezonedetector/TelephonyTimeZoneSuggestion.java +++ b/core/java/android/app/timezonedetector/TelephonyTimeZoneSuggestion.java @@ -263,7 +263,7 @@ public final class TelephonyTimeZoneSuggestion implements Parcelable { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (this == o) { return true; } diff --git a/core/java/android/app/usage/CacheQuotaHint.java b/core/java/android/app/usage/CacheQuotaHint.java index b5aed49f211e..0ccb058d11cf 100644 --- a/core/java/android/app/usage/CacheQuotaHint.java +++ b/core/java/android/app/usage/CacheQuotaHint.java @@ -44,14 +44,14 @@ public final class CacheQuotaHint implements Parcelable { * Create a new request. * @param builder A builder for this object. */ - public CacheQuotaHint(Builder builder) { + public CacheQuotaHint(@NonNull Builder builder) { this.mUuid = builder.mUuid; this.mUid = builder.mUid; this.mUsageStats = builder.mUsageStats; this.mQuota = builder.mQuota; } - public String getVolumeUuid() { + @Nullable public String getVolumeUuid() { return mUuid; } @@ -63,12 +63,12 @@ public final class CacheQuotaHint implements Parcelable { return mQuota; } - public UsageStats getUsageStats() { + @Nullable public UsageStats getUsageStats() { return mUsageStats; } @Override - public void writeToParcel(Parcel dest, int flags) { + public void writeToParcel(@NonNull Parcel dest, int flags) { dest.writeString(mUuid); dest.writeInt(mUid); dest.writeLong(mQuota); @@ -106,7 +106,7 @@ public final class CacheQuotaHint implements Parcelable { public Builder() { } - public Builder(CacheQuotaHint hint) { + public Builder(@NonNull CacheQuotaHint hint) { setVolumeUuid(hint.getVolumeUuid()); setUid(hint.getUid()); setUsageStats(hint.getUsageStats()); diff --git a/core/java/android/app/usage/UsageStats.java b/core/java/android/app/usage/UsageStats.java index f2a054d16ccb..dcecd9070a74 100644 --- a/core/java/android/app/usage/UsageStats.java +++ b/core/java/android/app/usage/UsageStats.java @@ -30,6 +30,7 @@ import static android.app.usage.UsageEvents.Event.ROLLOVER_FOREGROUND_SERVICE; import static android.app.usage.UsageEvents.Event.USER_INTERACTION; import android.annotation.SystemApi; +import android.annotation.TestApi; import android.compat.annotation.UnsupportedAppUsage; import android.os.Build; import android.os.Bundle; @@ -155,6 +156,7 @@ public final class UsageStats implements Parcelable { /** * {@hide} */ + @TestApi public UsageStats() { } diff --git a/core/java/android/bluetooth/BluetoothAudioConfig.java b/core/java/android/bluetooth/BluetoothAudioConfig.java index 9591a70b05d9..4c8b8c11fbc2 100644 --- a/core/java/android/bluetooth/BluetoothAudioConfig.java +++ b/core/java/android/bluetooth/BluetoothAudioConfig.java @@ -16,6 +16,7 @@ package android.bluetooth; +import android.annotation.Nullable; import android.os.Parcel; import android.os.Parcelable; @@ -39,7 +40,7 @@ public final class BluetoothAudioConfig implements Parcelable { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (o instanceof BluetoothAudioConfig) { BluetoothAudioConfig bac = (BluetoothAudioConfig) o; return (bac.mSampleRate == mSampleRate && bac.mChannelConfig == mChannelConfig diff --git a/core/java/android/bluetooth/BluetoothClass.java b/core/java/android/bluetooth/BluetoothClass.java index 905b0ceec4ee..603a7ff29ea3 100755 --- a/core/java/android/bluetooth/BluetoothClass.java +++ b/core/java/android/bluetooth/BluetoothClass.java @@ -16,6 +16,7 @@ package android.bluetooth; +import android.annotation.Nullable; import android.annotation.TestApi; import android.compat.annotation.UnsupportedAppUsage; import android.os.Build; @@ -72,7 +73,7 @@ public final class BluetoothClass implements Parcelable { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (o instanceof BluetoothClass) { return mClass == ((BluetoothClass) o).mClass; } diff --git a/core/java/android/bluetooth/BluetoothCodecConfig.java b/core/java/android/bluetooth/BluetoothCodecConfig.java index a52fc891790c..1d0bf97c34eb 100644 --- a/core/java/android/bluetooth/BluetoothCodecConfig.java +++ b/core/java/android/bluetooth/BluetoothCodecConfig.java @@ -18,6 +18,7 @@ package android.bluetooth; import android.annotation.IntDef; import android.annotation.NonNull; +import android.annotation.Nullable; import android.compat.annotation.UnsupportedAppUsage; import android.os.Parcel; import android.os.Parcelable; @@ -208,7 +209,7 @@ public final class BluetoothCodecConfig implements Parcelable { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (o instanceof BluetoothCodecConfig) { BluetoothCodecConfig other = (BluetoothCodecConfig) o; return (other.mCodecType == mCodecType diff --git a/core/java/android/bluetooth/BluetoothCodecStatus.java b/core/java/android/bluetooth/BluetoothCodecStatus.java index 7b567b4098e7..7764ebeb2e33 100644 --- a/core/java/android/bluetooth/BluetoothCodecStatus.java +++ b/core/java/android/bluetooth/BluetoothCodecStatus.java @@ -56,7 +56,7 @@ public final class BluetoothCodecStatus implements Parcelable { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (o instanceof BluetoothCodecStatus) { BluetoothCodecStatus other = (BluetoothCodecStatus) o; return (Objects.equals(other.mCodecConfig, mCodecConfig) diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java index 6287453f6399..1b0fe9dc2d78 100644 --- a/core/java/android/bluetooth/BluetoothDevice.java +++ b/core/java/android/bluetooth/BluetoothDevice.java @@ -974,7 +974,7 @@ public final class BluetoothDevice implements Parcelable { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (o instanceof BluetoothDevice) { return mAddress.equals(((BluetoothDevice) o).getAddress()); } diff --git a/core/java/android/bluetooth/BluetoothMasInstance.java b/core/java/android/bluetooth/BluetoothMasInstance.java index b64d0492faf5..eeaf08545146 100644 --- a/core/java/android/bluetooth/BluetoothMasInstance.java +++ b/core/java/android/bluetooth/BluetoothMasInstance.java @@ -16,6 +16,7 @@ package android.bluetooth; +import android.annotation.Nullable; import android.os.Parcel; import android.os.Parcelable; @@ -34,7 +35,7 @@ public final class BluetoothMasInstance implements Parcelable { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (o instanceof BluetoothMasInstance) { return mId == ((BluetoothMasInstance) o).mId; } diff --git a/core/java/android/bluetooth/le/AdvertiseData.java b/core/java/android/bluetooth/le/AdvertiseData.java index 573b93232642..397326c7fd40 100644 --- a/core/java/android/bluetooth/le/AdvertiseData.java +++ b/core/java/android/bluetooth/le/AdvertiseData.java @@ -124,7 +124,7 @@ public final class AdvertiseData implements Parcelable { * @hide */ @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { if (this == obj) { return true; } diff --git a/core/java/android/bluetooth/le/PeriodicAdvertisingReport.java b/core/java/android/bluetooth/le/PeriodicAdvertisingReport.java index 7a8c2c6716e3..54b953c25c27 100644 --- a/core/java/android/bluetooth/le/PeriodicAdvertisingReport.java +++ b/core/java/android/bluetooth/le/PeriodicAdvertisingReport.java @@ -148,7 +148,7 @@ public final class PeriodicAdvertisingReport implements Parcelable { } @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { if (this == obj) { return true; } diff --git a/core/java/android/bluetooth/le/ScanFilter.java b/core/java/android/bluetooth/le/ScanFilter.java index 7511fd051e41..51f63f7fb9fa 100644 --- a/core/java/android/bluetooth/le/ScanFilter.java +++ b/core/java/android/bluetooth/le/ScanFilter.java @@ -479,7 +479,7 @@ public final class ScanFilter implements Parcelable { } @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { if (this == obj) { return true; } diff --git a/core/java/android/bluetooth/le/ScanResult.java b/core/java/android/bluetooth/le/ScanResult.java index 855d34541707..57dad1a0259b 100644 --- a/core/java/android/bluetooth/le/ScanResult.java +++ b/core/java/android/bluetooth/le/ScanResult.java @@ -309,7 +309,7 @@ public final class ScanResult implements Parcelable { } @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { if (this == obj) { return true; } diff --git a/core/java/android/companion/AssociationRequest.java b/core/java/android/companion/AssociationRequest.java index 1f57c7d9d2f1..c55f0ff7da93 100644 --- a/core/java/android/companion/AssociationRequest.java +++ b/core/java/android/companion/AssociationRequest.java @@ -73,7 +73,7 @@ public final class AssociationRequest implements Parcelable { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; AssociationRequest that = (AssociationRequest) o; diff --git a/core/java/android/companion/BluetoothDeviceFilter.java b/core/java/android/companion/BluetoothDeviceFilter.java index cf9eeca0d739..48dab3b264fd 100644 --- a/core/java/android/companion/BluetoothDeviceFilter.java +++ b/core/java/android/companion/BluetoothDeviceFilter.java @@ -126,7 +126,7 @@ public final class BluetoothDeviceFilter implements DeviceFilter<BluetoothDevice } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; BluetoothDeviceFilter that = (BluetoothDeviceFilter) o; diff --git a/core/java/android/companion/BluetoothLeDeviceFilter.java b/core/java/android/companion/BluetoothLeDeviceFilter.java index 8c071fe99104..784e3a045bf4 100644 --- a/core/java/android/companion/BluetoothLeDeviceFilter.java +++ b/core/java/android/companion/BluetoothLeDeviceFilter.java @@ -184,7 +184,7 @@ public final class BluetoothLeDeviceFilter implements DeviceFilter<ScanResult> { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; BluetoothLeDeviceFilter that = (BluetoothLeDeviceFilter) o; diff --git a/core/java/android/companion/CompanionDeviceManager.java b/core/java/android/companion/CompanionDeviceManager.java index da4980bba3d0..c3c270e52eb6 100644 --- a/core/java/android/companion/CompanionDeviceManager.java +++ b/core/java/android/companion/CompanionDeviceManager.java @@ -21,7 +21,6 @@ import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.annotation.SystemService; -import android.annotation.TestApi; import android.app.Activity; import android.app.Application; import android.app.PendingIntent; @@ -131,7 +130,7 @@ public final class CompanionDeviceManager { * you use the {@link android.Manifest.permission#REQUEST_COMPANION_RUN_IN_BACKGROUND} and {@link * android.Manifest.permission#REQUEST_COMPANION_USE_DATA_IN_BACKGROUND} respectively. Note that these * special capabilities have a negative effect on the device's battery and user's data - * usage, therefore you should requested them when absolutely necessary.</p> + * usage, therefore you should request them when absolutely necessary.</p> * * <p>You can call {@link #getAssociations} to get the list of currently associated * devices, and {@link #disassociate} to remove an association. Consider doing so when the @@ -285,7 +284,6 @@ public final class CompanionDeviceManager { * @hide */ @SystemApi - @TestApi @RequiresPermission(android.Manifest.permission.MANAGE_COMPANION_DEVICES) public boolean isDeviceAssociatedForWifiConnection( @NonNull String packageName, diff --git a/core/java/android/companion/ICompanionDeviceDiscoveryService.aidl b/core/java/android/companion/ICompanionDeviceDiscoveryService.aidl index 5e3d46caae91..a630873c7f67 100644 --- a/core/java/android/companion/ICompanionDeviceDiscoveryService.aidl +++ b/core/java/android/companion/ICompanionDeviceDiscoveryService.aidl @@ -23,7 +23,7 @@ import com.android.internal.infra.AndroidFuture; /** @hide */ -interface ICompanionDeviceDiscoveryService { +oneway interface ICompanionDeviceDiscoveryService { void startDiscovery( in AssociationRequest request, in String callingPackage, diff --git a/core/java/android/content/ApexEnvironment.java b/core/java/android/content/ApexEnvironment.java index 9f15a425d92b..b4cc3c2bb156 100644 --- a/core/java/android/content/ApexEnvironment.java +++ b/core/java/android/content/ApexEnvironment.java @@ -18,7 +18,6 @@ package android.content; import android.annotation.NonNull; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.os.Environment; import android.os.UserHandle; @@ -31,7 +30,6 @@ import java.util.Objects; * @hide */ @SystemApi -@TestApi public class ApexEnvironment { private static final String APEX_DATA = "apexdata"; diff --git a/core/java/android/content/ComponentName.java b/core/java/android/content/ComponentName.java index 1967a01549c9..b1cee0cb00e0 100644 --- a/core/java/android/content/ComponentName.java +++ b/core/java/android/content/ComponentName.java @@ -312,7 +312,7 @@ public final class ComponentName implements Parcelable, Cloneable, Comparable<Co * same name, and if the classes that implement each component also have the same name. */ @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { try { if (obj != null) { ComponentName other = (ComponentName)obj; diff --git a/core/java/android/content/ContentProviderClient.java b/core/java/android/content/ContentProviderClient.java index d0f5ec467458..5af7861e1a20 100644 --- a/core/java/android/content/ContentProviderClient.java +++ b/core/java/android/content/ContentProviderClient.java @@ -21,7 +21,6 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.compat.annotation.UnsupportedAppUsage; import android.content.res.AssetFileDescriptor; import android.database.CrossProcessCursorWrapper; @@ -123,7 +122,6 @@ public class ContentProviderClient implements ContentInterface, AutoCloseable { * @hide */ @SystemApi - @TestApi @RequiresPermission(android.Manifest.permission.REMOVE_TASKS) public void setDetectNotResponding(@DurationMillisLong long timeoutMillis) { synchronized (ContentProviderClient.class) { diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java index c39f176ac0d1..fd7074c29891 100644 --- a/core/java/android/content/ContentResolver.java +++ b/core/java/android/content/ContentResolver.java @@ -4098,7 +4098,6 @@ public abstract class ContentResolver implements ContentInterface { * @hide */ @SystemApi - @TestApi // We can't accept an already-opened FD here, since these methods are // rewriting actual filesystem paths @SuppressLint("StreamFiles") @@ -4118,7 +4117,6 @@ public abstract class ContentResolver implements ContentInterface { * @hide */ @SystemApi - @TestApi // We can't accept an already-opened FD here, since these methods are // rewriting actual filesystem paths @SuppressLint("StreamFiles") diff --git a/core/java/android/content/ContentValues.java b/core/java/android/content/ContentValues.java index f9f4c5dc0d62..02a5ba13f2aa 100644 --- a/core/java/android/content/ContentValues.java +++ b/core/java/android/content/ContentValues.java @@ -93,7 +93,7 @@ public final class ContentValues implements Parcelable { } @Override - public boolean equals(Object object) { + public boolean equals(@Nullable Object object) { if (!(object instanceof ContentValues)) { return false; } diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 9c216a3ffd47..42fe0e1c465d 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -382,6 +382,7 @@ public abstract class Context { * {@link android.Manifest.permission#START_ACTIVITIES_FROM_BACKGROUND}. * @hide */ + @SystemApi public static final int BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS = 0x00100000; /** @@ -1848,7 +1849,6 @@ public abstract class Context { */ @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) @SystemApi - @TestApi public void startActivityAsUser(@RequiresPermission @NonNull Intent intent, @NonNull UserHandle user) { throw new RuntimeException("Not implemented. Must override in a subclass."); @@ -3508,6 +3508,7 @@ public abstract class Context { PERMISSION_SERVICE, LIGHTS_SERVICE, //@hide: PEOPLE_SERVICE, + //@hide: DEVICE_STATE_SERVICE, }) @Retention(RetentionPolicy.SOURCE) public @interface ServiceName {} @@ -3980,7 +3981,6 @@ public abstract class Context { * @hide */ @SystemApi - @TestApi @SuppressLint("ServiceName") public static final String STATUS_BAR_SERVICE = "statusbar"; @@ -4192,7 +4192,6 @@ public abstract class Context { * @hide */ @SystemApi - @TestApi public static final String ETHERNET_SERVICE = "ethernet"; /** @@ -4515,7 +4514,6 @@ public abstract class Context { * @see #getSystemService(String) * @hide */ - @TestApi @SystemApi public static final String PERMISSION_SERVICE = "permission"; @@ -4546,7 +4544,7 @@ public abstract class Context { * @see #getSystemService(String) * @hide */ - @SystemApi @TestApi + @SystemApi public static final String ROLLBACK_SERVICE = "rollback"; /** @@ -5020,7 +5018,7 @@ public abstract class Context { * @see android.os.BugreportManager * @hide */ - @SystemApi @TestApi + @SystemApi public static final String BUGREPORT_SERVICE = "bugreport"; /** @@ -5182,7 +5180,6 @@ public abstract class Context { * @hide */ @SystemApi - @TestApi public static final String APP_INTEGRITY_SERVICE = "app_integrity"; /** @@ -5246,6 +5243,14 @@ public abstract class Context { public static final String PEOPLE_SERVICE = "people"; /** + * Use with {@link #getSystemService(String)} to access device state service. + * + * @see #getSystemService(String) + * @hide + */ + public static final String DEVICE_STATE_SERVICE = "device_state"; + + /** * Determine whether the given permission is allowed for a particular * process and user ID running in the system. * @@ -5746,7 +5751,6 @@ public abstract class Context { * @hide */ @SystemApi - @TestApi @NonNull public Context createPackageContextAsUser( @NonNull String packageName, @CreatePackageOptions int flags, @NonNull UserHandle user) @@ -5765,7 +5769,6 @@ public abstract class Context { * @hide */ @SystemApi - @TestApi @NonNull public Context createContextAsUser(@NonNull UserHandle user, @CreatePackageOptions int flags) { if (Build.IS_ENG) { diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index 2f1254f90ccf..c62194b380dd 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -28,7 +28,6 @@ import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; import android.annotation.SuppressLint; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.app.AppGlobals; import android.compat.annotation.UnsupportedAppUsage; import android.content.pm.ActivityInfo; @@ -1748,7 +1747,6 @@ public class Intent implements Parcelable, Cloneable { * @hide */ @SystemApi - @TestApi public static final String EXTRA_ORIGINATING_UID = "android.intent.extra.ORIGINATING_UID"; @@ -1979,7 +1977,6 @@ public class Intent implements Parcelable, Cloneable { @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) @SystemApi - @TestApi public static final String ACTION_MANAGE_DEFAULT_APP = "android.intent.action.MANAGE_DEFAULT_APP"; @@ -1994,7 +1991,6 @@ public class Intent implements Parcelable, Cloneable { * @hide */ @SystemApi - @TestApi public static final String EXTRA_ROLE_NAME = "android.intent.extra.ROLE_NAME"; /** @@ -2541,7 +2537,7 @@ public class Intent implements Parcelable, Cloneable { * * @hide */ - @SystemApi @TestApi + @SystemApi @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_ROLLBACK_COMMITTED = "android.intent.action.ROLLBACK_COMMITTED"; @@ -2744,7 +2740,6 @@ public class Intent implements Parcelable, Cloneable { * </ul> * * <p class="note">This is a protected intent that can only be sent by the system. - * @hide */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_PACKAGE_STARTABLE = "android.intent.action.PACKAGE_STARTABLE"; @@ -2755,13 +2750,13 @@ public class Intent implements Parcelable, Cloneable { * <ul> * <li> {@link #EXTRA_UID} containing the integer uid assigned to the package. </li> * <li> {@link #EXTRA_PACKAGE_NAME} containing the package name. </li> - * <li> {@link #EXTRA_REASON} containing the integer indicating the reason for the state change, + * <li> {@link #EXTRA_UNSTARTABLE_REASON} containing the integer indicating the reason for + * the state change, * @see PackageManager.UnstartableReason * </li> * </ul> * * <p class="note">This is a protected intent that can only be sent by the system. - * @hide */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_PACKAGE_UNSTARTABLE = @@ -2776,7 +2771,6 @@ public class Intent implements Parcelable, Cloneable { * </ul> * * <p class="note">This is a protected intent that can only be sent by the system. - * @hide */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_PACKAGE_FULLY_LOADED = @@ -6015,6 +6009,13 @@ public class Intent implements Parcelable, Cloneable { */ public static final String EXTRA_LOCUS_ID = "android.intent.extra.LOCUS_ID"; + /** + * Intent extra: the reason that the package associated with this intent has become unstartable. + * + * <p>Type: String + */ + public static final String EXTRA_UNSTARTABLE_REASON = "android.intent.extra.UNSTARTABLE_REASON"; + // --------------------------------------------------------------------- // --------------------------------------------------------------------- // Intent flags (see mFlags variable). @@ -10373,7 +10374,7 @@ public class Intent implements Parcelable, Cloneable { } @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { if (obj instanceof FilterComparison) { Intent other = ((FilterComparison)obj).mIntent; return mIntent.filterEquals(other); diff --git a/core/java/android/content/IntentFilter.java b/core/java/android/content/IntentFilter.java index 7fe29a920931..5240ab4e476f 100644 --- a/core/java/android/content/IntentFilter.java +++ b/core/java/android/content/IntentFilter.java @@ -1144,7 +1144,7 @@ public class IntentFilter implements Parcelable { } @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { if (obj instanceof AuthorityEntry) { final AuthorityEntry other = (AuthorityEntry)obj; return match(other); diff --git a/core/java/android/content/IntentSender.java b/core/java/android/content/IntentSender.java index f40dc298d560..858d1e498783 100644 --- a/core/java/android/content/IntentSender.java +++ b/core/java/android/content/IntentSender.java @@ -16,6 +16,7 @@ package android.content; +import android.annotation.Nullable; import android.app.ActivityManager; import android.compat.annotation.UnsupportedAppUsage; import android.os.Bundle; @@ -27,7 +28,6 @@ import android.os.RemoteException; import android.os.UserHandle; import android.util.AndroidException; - /** * A description of an Intent and target action to perform with it. * The returned object can be @@ -284,7 +284,7 @@ public class IntentSender implements Parcelable { * same package. */ @Override - public boolean equals(Object otherObj) { + public boolean equals(@Nullable Object otherObj) { if (otherObj instanceof IntentSender) { return mTarget.asBinder().equals(((IntentSender)otherObj) .mTarget.asBinder()); diff --git a/core/java/android/content/LocusId.java b/core/java/android/content/LocusId.java index 98e71f07407e..a04fbd1e7ed4 100644 --- a/core/java/android/content/LocusId.java +++ b/core/java/android/content/LocusId.java @@ -16,6 +16,7 @@ package android.content; import android.annotation.NonNull; +import android.annotation.Nullable; import android.os.Parcel; import android.os.Parcelable; import android.view.contentcapture.ContentCaptureManager; @@ -92,7 +93,7 @@ public final class LocusId implements Parcelable { } @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; diff --git a/core/java/android/content/PeriodicSync.java b/core/java/android/content/PeriodicSync.java index a075148a6293..432e81bad019 100644 --- a/core/java/android/content/PeriodicSync.java +++ b/core/java/android/content/PeriodicSync.java @@ -16,10 +16,11 @@ package android.content; -import android.os.Parcelable; +import android.accounts.Account; +import android.annotation.Nullable; import android.os.Bundle; import android.os.Parcel; -import android.accounts.Account; +import android.os.Parcelable; import java.util.Objects; @@ -117,7 +118,7 @@ public class PeriodicSync implements Parcelable { }; @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (o == this) { return true; } diff --git a/core/java/android/content/RestrictionEntry.java b/core/java/android/content/RestrictionEntry.java index 8f08fdec7806..63fcb49fff1b 100644 --- a/core/java/android/content/RestrictionEntry.java +++ b/core/java/android/content/RestrictionEntry.java @@ -17,6 +17,7 @@ package android.content; import android.annotation.ArrayRes; +import android.annotation.Nullable; import android.os.Parcel; import android.os.Parcelable; @@ -452,7 +453,7 @@ public class RestrictionEntry implements Parcelable { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (o == this) return true; if (!(o instanceof RestrictionEntry)) return false; final RestrictionEntry other = (RestrictionEntry) o; diff --git a/core/java/android/content/SyncAdapterType.java b/core/java/android/content/SyncAdapterType.java index 7bcdbfd1085c..ffcdf53ca84d 100644 --- a/core/java/android/content/SyncAdapterType.java +++ b/core/java/android/content/SyncAdapterType.java @@ -176,7 +176,7 @@ public class SyncAdapterType implements Parcelable { return new SyncAdapterType(authority, accountType); } - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (o == this) return true; if (!(o instanceof SyncAdapterType)) return false; final SyncAdapterType other = (SyncAdapterType)o; diff --git a/core/java/android/content/integrity/AppIntegrityManager.java b/core/java/android/content/integrity/AppIntegrityManager.java index 4db4c7316433..1196064768e8 100644 --- a/core/java/android/content/integrity/AppIntegrityManager.java +++ b/core/java/android/content/integrity/AppIntegrityManager.java @@ -36,7 +36,6 @@ import java.util.List; * * @hide */ -@TestApi @SystemApi @SystemService(Context.APP_INTEGRITY_SERVICE) public class AppIntegrityManager { diff --git a/core/java/android/content/integrity/AtomicFormula.java b/core/java/android/content/integrity/AtomicFormula.java index f363a54edc16..a64f39367e26 100644 --- a/core/java/android/content/integrity/AtomicFormula.java +++ b/core/java/android/content/integrity/AtomicFormula.java @@ -20,6 +20,7 @@ import static com.android.internal.util.Preconditions.checkArgument; import android.annotation.IntDef; import android.annotation.NonNull; +import android.annotation.Nullable; import android.os.Parcel; import android.os.Parcelable; @@ -240,7 +241,7 @@ public abstract class AtomicFormula extends IntegrityFormula { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (this == o) { return true; } @@ -426,7 +427,7 @@ public abstract class AtomicFormula extends IntegrityFormula { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (this == o) { return true; } @@ -594,7 +595,7 @@ public abstract class AtomicFormula extends IntegrityFormula { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (this == o) { return true; } diff --git a/core/java/android/content/integrity/CompoundFormula.java b/core/java/android/content/integrity/CompoundFormula.java index 14b1197d7f45..160bec9a51b9 100644 --- a/core/java/android/content/integrity/CompoundFormula.java +++ b/core/java/android/content/integrity/CompoundFormula.java @@ -20,6 +20,7 @@ import static com.android.internal.util.Preconditions.checkArgument; import android.annotation.IntDef; import android.annotation.NonNull; +import android.annotation.Nullable; import android.os.Parcel; import android.os.Parcelable; @@ -158,7 +159,7 @@ public final class CompoundFormula extends IntegrityFormula implements Parcelabl } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (this == o) { return true; } diff --git a/core/java/android/content/integrity/IntegrityFormula.java b/core/java/android/content/integrity/IntegrityFormula.java index fc177721240c..d965ef5c71a8 100644 --- a/core/java/android/content/integrity/IntegrityFormula.java +++ b/core/java/android/content/integrity/IntegrityFormula.java @@ -19,7 +19,6 @@ package android.content.integrity; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.content.integrity.AtomicFormula.BooleanAtomicFormula; import android.content.integrity.AtomicFormula.LongAtomicFormula; import android.content.integrity.AtomicFormula.StringAtomicFormula; @@ -38,7 +37,6 @@ import java.util.Arrays; * @hide */ @SystemApi -@TestApi @VisibleForTesting public abstract class IntegrityFormula { diff --git a/core/java/android/content/integrity/Rule.java b/core/java/android/content/integrity/Rule.java index d29e6df5ec39..391d1d04d553 100644 --- a/core/java/android/content/integrity/Rule.java +++ b/core/java/android/content/integrity/Rule.java @@ -20,8 +20,8 @@ import static com.android.internal.util.Preconditions.checkArgument; import android.annotation.IntDef; import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.os.Parcel; import android.os.Parcelable; @@ -38,7 +38,6 @@ import java.util.Objects; * * @hide */ -@TestApi @SystemApi @VisibleForTesting public final class Rule implements Parcelable { @@ -115,7 +114,7 @@ public final class Rule implements Parcelable { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (this == o) { return true; } diff --git a/core/java/android/content/integrity/RuleSet.java b/core/java/android/content/integrity/RuleSet.java index e121ff8bbcaa..b423b54a7d92 100644 --- a/core/java/android/content/integrity/RuleSet.java +++ b/core/java/android/content/integrity/RuleSet.java @@ -18,7 +18,6 @@ package android.content.integrity; import android.annotation.NonNull; import android.annotation.SystemApi; -import android.annotation.TestApi; import java.util.ArrayList; import java.util.Collections; @@ -30,7 +29,6 @@ import java.util.Objects; * * @hide */ -@TestApi @SystemApi public class RuleSet { private final String mVersion; diff --git a/core/java/android/content/om/OverlayableInfo.java b/core/java/android/content/om/OverlayableInfo.java index 5923907b11e7..6bd42d99f37a 100644 --- a/core/java/android/content/om/OverlayableInfo.java +++ b/core/java/android/content/om/OverlayableInfo.java @@ -82,7 +82,7 @@ public final class OverlayableInfo { @Override @DataClass.Generated.Member - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { // You can override field equality logic by defining either of the methods like: // boolean fieldNameEquals(OverlayableInfo other) { ... } // boolean fieldNameEquals(FieldType otherValue) { ... } diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java index ea4b76257ab7..b371141ca9c1 100644 --- a/core/java/android/content/pm/ActivityInfo.java +++ b/core/java/android/content/pm/ActivityInfo.java @@ -1419,6 +1419,13 @@ public class ActivityInfo extends ComponentInfo implements Parcelable { public static final class WindowLayout { public WindowLayout(int width, float widthFraction, int height, float heightFraction, int gravity, int minWidth, int minHeight) { + this(width, widthFraction, height, heightFraction, gravity, minWidth, minHeight, + null /* windowLayoutAffinity */); + } + + /** @hide */ + public WindowLayout(int width, float widthFraction, int height, float heightFraction, + int gravity, int minWidth, int minHeight, String windowLayoutAffinity) { this.width = width; this.widthFraction = widthFraction; this.height = height; @@ -1426,6 +1433,7 @@ public class ActivityInfo extends ComponentInfo implements Parcelable { this.gravity = gravity; this.minWidth = minWidth; this.minHeight = minHeight; + this.windowLayoutAffinity = windowLayoutAffinity; } /** @hide */ @@ -1506,6 +1514,8 @@ public class ActivityInfo extends ComponentInfo implements Parcelable { /** * Affinity of window layout parameters. Activities with the same UID and window layout * affinity will share the same window dimension record. + * + * @attr ref android.R.styleable#AndroidManifestLayout_windowLayoutAffinity * @hide */ public String windowLayoutAffinity; diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java index d5f2c12e8462..8f4fc261df37 100644 --- a/core/java/android/content/pm/ApplicationInfo.java +++ b/core/java/android/content/pm/ApplicationInfo.java @@ -1141,7 +1141,6 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { * @hide */ @SystemApi - @TestApi public int targetSandboxVersion; /** diff --git a/core/java/android/content/pm/KeySet.java b/core/java/android/content/pm/KeySet.java index 5c1d35e372ad..fd459e662845 100644 --- a/core/java/android/content/pm/KeySet.java +++ b/core/java/android/content/pm/KeySet.java @@ -16,6 +16,7 @@ package android.content.pm; +import android.annotation.Nullable; import android.os.IBinder; import android.os.Parcel; import android.os.Parcelable; @@ -45,7 +46,7 @@ public class KeySet implements Parcelable { /** @hide */ @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (o instanceof KeySet) { KeySet ks = (KeySet) o; return token == ks.token; diff --git a/core/java/android/content/pm/ModuleInfo.java b/core/java/android/content/pm/ModuleInfo.java index a6db662bcc43..a7306a311ad8 100644 --- a/core/java/android/content/pm/ModuleInfo.java +++ b/core/java/android/content/pm/ModuleInfo.java @@ -130,7 +130,7 @@ public final class ModuleInfo implements Parcelable { } @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { if (!(obj instanceof ModuleInfo)) { return false; } diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java index f03425b9e117..e2f85282948a 100644 --- a/core/java/android/content/pm/PackageInstaller.java +++ b/core/java/android/content/pm/PackageInstaller.java @@ -1722,7 +1722,6 @@ public class PackageInstaller { * * @hide */ - @TestApi @SystemApi @RequiresPermission(android.Manifest.permission.INSTALL_GRANT_RUNTIME_PERMISSIONS) public void setGrantedRuntimePermissions(String[] permissions) { @@ -1794,7 +1793,7 @@ public class PackageInstaller { * @see SessionParams#setEnableRollback(boolean, int) * @hide */ - @SystemApi @TestApi + @SystemApi public void setEnableRollback(boolean enable) { if (enable) { installFlags |= PackageManager.INSTALL_ENABLE_ROLLBACK; @@ -1818,7 +1817,7 @@ public class PackageInstaller { * @param dataPolicy the rollback data policy for this session * @hide */ - @SystemApi @TestApi + @SystemApi public void setEnableRollback(boolean enable, @PackageManager.RollbackDataPolicy int dataPolicy) { if (enable) { @@ -1841,7 +1840,7 @@ public class PackageInstaller { } /** {@hide} */ - @SystemApi @TestApi + @SystemApi public void setRequestDowngrade(boolean requestDowngrade) { if (requestDowngrade) { installFlags |= PackageManager.INSTALL_REQUEST_DOWNGRADE; @@ -1880,7 +1879,6 @@ public class PackageInstaller { /** {@hide} */ @SystemApi - @TestApi public void setInstallAsInstantApp(boolean isInstantApp) { if (isInstantApp) { installFlags |= PackageManager.INSTALL_INSTANT_APP; @@ -1965,7 +1963,7 @@ public class PackageInstaller { * * {@hide} */ - @SystemApi @TestApi + @SystemApi @RequiresPermission(Manifest.permission.INSTALL_PACKAGES) public void setStaged() { this.isStaged = true; @@ -1976,7 +1974,7 @@ public class PackageInstaller { * * {@hide} */ - @SystemApi @TestApi + @SystemApi @RequiresPermission(Manifest.permission.INSTALL_PACKAGES) public void setInstallAsApex() { installFlags |= PackageManager.INSTALL_APEX; @@ -2480,7 +2478,6 @@ public class PackageInstaller { * * @hide */ - @TestApi @SystemApi public @NonNull Set<String> getWhitelistedRestrictedPermissions() { if ((installFlags & PackageManager.INSTALL_ALL_WHITELIST_RESTRICTED_PERMISSIONS) != 0) { @@ -2504,7 +2501,6 @@ public class PackageInstaller { * * @hide */ - @TestApi @SystemApi public int getAutoRevokePermissionsMode() { return autoRevokePermissionsMode; @@ -2633,7 +2629,7 @@ public class PackageInstaller { * * @hide */ - @SystemApi @TestApi + @SystemApi @PackageManager.RollbackDataPolicy public int getRollbackDataPolicy() { return rollbackDataPolicy; diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 1a992f519286..fc7b3e011cad 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -120,7 +120,6 @@ public abstract class PackageManager { * @hide */ @SystemApi - @TestApi public interface OnPermissionsChangedListener { /** @@ -479,7 +478,6 @@ public abstract class PackageManager { * @hide */ @SystemApi - @TestApi public static final int MATCH_FACTORY_ONLY = 0x00200000; /** @@ -611,7 +609,6 @@ public abstract class PackageManager { * @hide */ @SystemApi - @TestApi public static final int MODULE_APEX_NAME = 0x00000001; /** @hide */ @@ -3319,7 +3316,6 @@ public abstract class PackageManager { * @hide */ @SystemApi - @TestApi public static final int FLAG_PERMISSION_USER_SET = 1 << 0; /** @@ -3330,7 +3326,6 @@ public abstract class PackageManager { * @hide */ @SystemApi - @TestApi public static final int FLAG_PERMISSION_USER_FIXED = 1 << 1; /** @@ -3341,7 +3336,6 @@ public abstract class PackageManager { * @hide */ @SystemApi - @TestApi public static final int FLAG_PERMISSION_POLICY_FIXED = 1 << 2; /** @@ -3358,7 +3352,6 @@ public abstract class PackageManager { */ @Deprecated @SystemApi - @TestApi public static final int FLAG_PERMISSION_REVOKE_ON_UPGRADE = 1 << 3; /** @@ -3368,7 +3361,6 @@ public abstract class PackageManager { * @hide */ @SystemApi - @TestApi public static final int FLAG_PERMISSION_SYSTEM_FIXED = 1 << 4; /** @@ -3380,7 +3372,6 @@ public abstract class PackageManager { * @hide */ @SystemApi - @TestApi public static final int FLAG_PERMISSION_GRANTED_BY_DEFAULT = 1 << 5; /** @@ -3390,7 +3381,6 @@ public abstract class PackageManager { * @hide */ @SystemApi - @TestApi public static final int FLAG_PERMISSION_REVIEW_REQUIRED = 1 << 6; /** @@ -3428,7 +3418,6 @@ public abstract class PackageManager { * * @hide */ - @TestApi @SystemApi public static final int FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT = 1 << 11; @@ -3440,7 +3429,6 @@ public abstract class PackageManager { * * @hide */ - @TestApi @SystemApi public static final int FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT = 1 << 12; @@ -3453,7 +3441,6 @@ public abstract class PackageManager { * * @hide */ - @TestApi @SystemApi public static final int FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT = 1 << 13; @@ -3466,7 +3453,6 @@ public abstract class PackageManager { * * @hide */ - @TestApi @SystemApi public static final int FLAG_PERMISSION_APPLY_RESTRICTION = 1 << 14; @@ -3476,7 +3462,6 @@ public abstract class PackageManager { * @hide */ @SystemApi - @TestApi public static final int FLAG_PERMISSION_GRANTED_BY_ROLE = 1 << 15; /** @@ -3488,7 +3473,6 @@ public abstract class PackageManager { * @hide */ @SystemApi - @TestApi public static final int FLAG_PERMISSION_REVOKED_COMPAT = FLAG_PERMISSION_REVOKE_ON_UPGRADE; /** @@ -3498,7 +3482,6 @@ public abstract class PackageManager { * @hide */ @SystemApi - @TestApi public static final int FLAG_PERMISSION_ONE_TIME = 1 << 16; /** @@ -3516,7 +3499,6 @@ public abstract class PackageManager { * * @hide */ - @TestApi @SystemApi public static final int FLAG_PERMISSION_RESTRICTION_ROLE_EXEMPT = 1 << 18; @@ -3790,8 +3772,8 @@ public abstract class PackageManager { * @hide */ @IntDef({UNSTARTABLE_REASON_UNKNOWN, - UNSTARTABLE_REASON_DATALOADER_TRANSPORT, - UNSTARTABLE_REASON_DATALOADER_STORAGE + UNSTARTABLE_REASON_CONNECTION_ERROR, + UNSTARTABLE_REASON_INSUFFICIENT_STORAGE }) @Retention(RetentionPolicy.SOURCE) public @interface UnstartableReason {} @@ -3800,23 +3782,20 @@ public abstract class PackageManager { * Unstartable state with no root cause specified. E.g., data loader seeing missing pages but * unclear about the cause. This corresponds to a generic alert window shown to the user when * the user attempts to launch the app. - * @hide */ public static final int UNSTARTABLE_REASON_UNKNOWN = 0; /** - * Unstartable state after hint from dataloader of issues with the transport layer. - * This corresponds to an alert window shown to the user indicating network errors. - * @hide + * Unstartable state due to connection issues that interrupt package loading. + * This corresponds to an alert window shown to the user indicating connection errors. */ - public static final int UNSTARTABLE_REASON_DATALOADER_TRANSPORT = 1; + public static final int UNSTARTABLE_REASON_CONNECTION_ERROR = 1; /** * Unstartable state after encountering storage limitations. * This corresponds to an alert window indicating limited storage. - * @hide */ - public static final int UNSTARTABLE_REASON_DATALOADER_STORAGE = 2; + public static final int UNSTARTABLE_REASON_INSUFFICIENT_STORAGE = 2; /** {@hide} */ public int getUserId() { @@ -4086,7 +4065,7 @@ public abstract class PackageManager { * * @hide */ - @TestApi @SystemApi + @SystemApi public abstract boolean arePermissionsIndividuallyControlled(); /** @@ -4337,7 +4316,6 @@ public abstract class PackageManager { * @hide */ @NonNull - @TestApi @SystemApi @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) public abstract List<PackageInfo> getInstalledPackagesAsUser(@PackageInfoFlags int flags, @@ -4498,7 +4476,6 @@ public abstract class PackageManager { * * @hide */ - @TestApi @SystemApi @RequiresPermission(android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS) public abstract void grantRuntimePermission(@NonNull String packageName, @@ -4525,7 +4502,6 @@ public abstract class PackageManager { * * @hide */ - @TestApi @SystemApi @RequiresPermission(android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS) public abstract void revokeRuntimePermission(@NonNull String packageName, @@ -4553,7 +4529,6 @@ public abstract class PackageManager { * * @hide */ - @TestApi @SystemApi @RequiresPermission(android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS) public void revokeRuntimePermission(@NonNull String packageName, @@ -4572,7 +4547,6 @@ public abstract class PackageManager { * @hide */ @SystemApi - @TestApi @RequiresPermission(anyOf = { android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS, android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS, @@ -4595,7 +4569,6 @@ public abstract class PackageManager { * @hide */ @SystemApi - @TestApi @RequiresPermission(anyOf = { android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS, android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS @@ -6443,7 +6416,6 @@ public abstract class PackageManager { * @hide */ @Nullable - @TestApi @SystemApi @RequiresPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL) public abstract String getDefaultBrowserPackageNameAsUser(@UserIdInt int userId); @@ -7097,7 +7069,6 @@ public abstract class PackageManager { * @hide */ @SystemApi - @TestApi @RequiresPermission(Manifest.permission.OBSERVE_GRANT_REVOKE_PERMISSIONS) public abstract void addOnPermissionsChangeListener( @NonNull OnPermissionsChangedListener listener); @@ -7110,7 +7081,6 @@ public abstract class PackageManager { * @hide */ @SystemApi - @TestApi @RequiresPermission(Manifest.permission.OBSERVE_GRANT_REVOKE_PERMISSIONS) public abstract void removeOnPermissionsChangeListener( @NonNull OnPermissionsChangedListener listener); @@ -8129,7 +8099,6 @@ public abstract class PackageManager { * @hide */ @SystemApi - @TestApi @Nullable public String getIncidentReportApproverPackageName() { throw new UnsupportedOperationException( @@ -8271,7 +8240,7 @@ public abstract class PackageManager { } @Override - public boolean equals(Object rval) { + public boolean equals(@Nullable Object rval) { if (rval == null) { return false; } @@ -8374,7 +8343,7 @@ public abstract class PackageManager { } @Override - public boolean equals(Object rval) { + public boolean equals(@Nullable Object rval) { if (rval == null) { return false; } diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index e108451b54fb..177f691a97c1 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -6473,7 +6473,7 @@ public class PackageParser { }; @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (this == o) return true; if (!(o instanceof SigningDetails)) return false; diff --git a/core/java/android/content/pm/PackageStats.java b/core/java/android/content/pm/PackageStats.java index 7c1252737f09..81cfc078c035 100644 --- a/core/java/android/content/pm/PackageStats.java +++ b/core/java/android/content/pm/PackageStats.java @@ -16,6 +16,7 @@ package android.content.pm; +import android.annotation.Nullable; import android.app.usage.StorageStatsManager; import android.compat.annotation.UnsupportedAppUsage; import android.os.Build; @@ -186,7 +187,7 @@ public class PackageStats implements Parcelable { } @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { if (!(obj instanceof PackageStats)) { return false; } diff --git a/core/java/android/content/pm/PackageUserState.java b/core/java/android/content/pm/PackageUserState.java index 3ed21b087972..2fca980e764c 100644 --- a/core/java/android/content/pm/PackageUserState.java +++ b/core/java/android/content/pm/PackageUserState.java @@ -369,7 +369,7 @@ public class PackageUserState { } @Override - final public boolean equals(Object obj) { + final public boolean equals(@Nullable Object obj) { if (!(obj instanceof PackageUserState)) { return false; } @@ -527,7 +527,7 @@ public class PackageUserState { } @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { if (this == obj) { return true; } diff --git a/core/java/android/content/pm/PermissionInfo.java b/core/java/android/content/pm/PermissionInfo.java index 5d4c843d2eab..e730fee7f812 100644 --- a/core/java/android/content/pm/PermissionInfo.java +++ b/core/java/android/content/pm/PermissionInfo.java @@ -160,7 +160,6 @@ public class PermissionInfo extends PackageItemInfo implements Parcelable { * @hide */ @SystemApi - @TestApi public static final int PROTECTION_FLAG_OEM = 0x4000; /** @@ -181,7 +180,6 @@ public class PermissionInfo extends PackageItemInfo implements Parcelable { * @hide */ @SystemApi - @TestApi public static final int PROTECTION_FLAG_SYSTEM_TEXT_CLASSIFIER = 0x10000; /** @@ -192,7 +190,6 @@ public class PermissionInfo extends PackageItemInfo implements Parcelable { * @hide */ @SystemApi - @TestApi public static final int PROTECTION_FLAG_WELLBEING = 0x20000; /** @@ -202,7 +199,6 @@ public class PermissionInfo extends PackageItemInfo implements Parcelable { * @hide */ @SystemApi - @TestApi public static final int PROTECTION_FLAG_DOCUMENTER = 0x40000; /** @@ -212,7 +208,6 @@ public class PermissionInfo extends PackageItemInfo implements Parcelable { * @hide */ @SystemApi - @TestApi public static final int PROTECTION_FLAG_CONFIGURATOR = 0x80000; /** @@ -223,7 +218,6 @@ public class PermissionInfo extends PackageItemInfo implements Parcelable { * @hide */ @SystemApi - @TestApi public static final int PROTECTION_FLAG_INCIDENT_REPORT_APPROVER = 0x100000; /** @@ -234,7 +228,6 @@ public class PermissionInfo extends PackageItemInfo implements Parcelable { * @hide */ @SystemApi - @TestApi public static final int PROTECTION_FLAG_APP_PREDICTOR = 0x200000; /** @@ -245,7 +238,6 @@ public class PermissionInfo extends PackageItemInfo implements Parcelable { * @hide */ @SystemApi - @TestApi public static final int PROTECTION_FLAG_COMPANION = 0x800000; /** @@ -256,7 +248,6 @@ public class PermissionInfo extends PackageItemInfo implements Parcelable { * @hide */ @SystemApi - @TestApi public static final int PROTECTION_FLAG_RETAIL_DEMO = 0x1000000; /** @hide */ @@ -340,7 +331,6 @@ public class PermissionInfo extends PackageItemInfo implements Parcelable { * value of {@link android.R.attr#permissionFlags}. * @hide */ - @TestApi @SystemApi public static final int FLAG_REMOVED = 1<<1; @@ -436,7 +426,6 @@ public class PermissionInfo extends PackageItemInfo implements Parcelable { * @hide */ @SystemApi - @TestApi public final @Nullable String backgroundPermission; /** diff --git a/core/java/android/content/pm/Signature.java b/core/java/android/content/pm/Signature.java index b2875e91f80a..fc5d94527750 100644 --- a/core/java/android/content/pm/Signature.java +++ b/core/java/android/content/pm/Signature.java @@ -16,6 +16,7 @@ package android.content.pm; +import android.annotation.Nullable; import android.compat.annotation.UnsupportedAppUsage; import android.os.Parcel; import android.os.Parcelable; @@ -248,7 +249,7 @@ public class Signature implements Parcelable { } @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { try { if (obj != null) { Signature other = (Signature)obj; diff --git a/core/java/android/content/pm/VerifierDeviceIdentity.java b/core/java/android/content/pm/VerifierDeviceIdentity.java index f29aaece6c46..3ce0b65e9f95 100644 --- a/core/java/android/content/pm/VerifierDeviceIdentity.java +++ b/core/java/android/content/pm/VerifierDeviceIdentity.java @@ -16,6 +16,7 @@ package android.content.pm; +import android.annotation.Nullable; import android.os.Parcel; import android.os.Parcelable; @@ -194,7 +195,7 @@ public class VerifierDeviceIdentity implements Parcelable { } @Override - public boolean equals(Object other) { + public boolean equals(@Nullable Object other) { if (!(other instanceof VerifierDeviceIdentity)) { return false; } diff --git a/core/java/android/content/pm/VersionedPackage.java b/core/java/android/content/pm/VersionedPackage.java index 2987557a54b4..fa50fcaa56b3 100644 --- a/core/java/android/content/pm/VersionedPackage.java +++ b/core/java/android/content/pm/VersionedPackage.java @@ -17,6 +17,7 @@ package android.content.pm; import android.annotation.IntRange; import android.annotation.NonNull; +import android.annotation.Nullable; import android.os.Parcel; import android.os.Parcelable; @@ -96,7 +97,7 @@ public final class VersionedPackage implements Parcelable { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { return o instanceof VersionedPackage && ((VersionedPackage) o).mPackageName.equals(mPackageName) && ((VersionedPackage) o).mVersionCode == mVersionCode; diff --git a/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java b/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java index 8c0bfef19d0f..511ee5d9580d 100644 --- a/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java +++ b/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java @@ -368,8 +368,8 @@ public class ParsedActivityUtils { } result = intentResult; } else if (!isReceiver && !isAlias && parser.getName().equals("layout")) { - ParseResult<ActivityInfo.WindowLayout> layoutResult = parseLayout(resources, parser, - input); + ParseResult<ActivityInfo.WindowLayout> layoutResult = + parseActivityWindowLayout(resources, parser, input); if (layoutResult.isSuccess()) { activity.windowLayout = layoutResult.getResult(); } @@ -383,7 +383,8 @@ public class ParsedActivityUtils { } } - ParseResult<ActivityInfo.WindowLayout> layoutResult = resolveWindowLayout(activity, input); + ParseResult<ActivityInfo.WindowLayout> layoutResult = + resolveActivityWindowLayout(activity, input); if (layoutResult.isError()) { return input.error(layoutResult); } @@ -468,7 +469,7 @@ public class ParsedActivityUtils { } @NonNull - private static ParseResult<ActivityInfo.WindowLayout> parseLayout(Resources res, + private static ParseResult<ActivityInfo.WindowLayout> parseActivityWindowLayout(Resources res, AttributeSet attrs, ParseInput input) { TypedArray sw = res.obtainAttributes(attrs, R.styleable.AndroidManifestLayout); try { @@ -496,8 +497,13 @@ public class ParsedActivityUtils { int minWidth = sw.getDimensionPixelSize(R.styleable.AndroidManifestLayout_minWidth, -1); int minHeight = sw.getDimensionPixelSize(R.styleable.AndroidManifestLayout_minHeight, -1); - return input.success(new ActivityInfo.WindowLayout(width, widthFraction, height, - heightFraction, gravity, minWidth, minHeight)); + String windowLayoutAffinity = + sw.getNonConfigurationString( + R.styleable.AndroidManifestLayout_windowLayoutAffinity, 0); + final ActivityInfo.WindowLayout windowLayout = new ActivityInfo.WindowLayout(width, + widthFraction, height, heightFraction, gravity, minWidth, minHeight, + windowLayoutAffinity); + return input.success(windowLayout); } finally { sw.recycle(); } @@ -509,7 +515,7 @@ public class ParsedActivityUtils { * <p>{@link ActivityInfo.WindowLayout#windowLayoutAffinity} has a fallback metadata used in * Android R and some variants of pre-R. */ - private static ParseResult<ActivityInfo.WindowLayout> resolveWindowLayout( + private static ParseResult<ActivityInfo.WindowLayout> resolveActivityWindowLayout( ParsedActivity activity, ParseInput input) { // There isn't a metadata for us to fall back. Whatever is in layout is correct. if (activity.metaData == null || !activity.metaData.containsKey( @@ -528,9 +534,10 @@ public class ParsedActivityUtils { if (layout == null) { layout = new ActivityInfo.WindowLayout(-1 /* width */, -1 /* widthFraction */, -1 /* height */, -1 /* heightFraction */, Gravity.NO_GRAVITY, - -1 /* minWidth */, -1 /* minHeight */); + -1 /* minWidth */, -1 /* minHeight */, windowLayoutAffinity); + } else { + layout.windowLayoutAffinity = windowLayoutAffinity; } - layout.windowLayoutAffinity = windowLayoutAffinity; return input.success(layout); } } diff --git a/core/java/android/content/pm/parsing/component/ParsedPermission.java b/core/java/android/content/pm/parsing/component/ParsedPermission.java index ced322649c66..f99a0b1dcadb 100644 --- a/core/java/android/content/pm/parsing/component/ParsedPermission.java +++ b/core/java/android/content/pm/parsing/component/ParsedPermission.java @@ -54,22 +54,6 @@ public class ParsedPermission extends ParsedComponent { this.parsedPermissionGroup = other.parsedPermissionGroup; } - public ParsedPermission(ParsedPermission other, PermissionInfo pendingPermissionInfo, - String packageName, String name) { - this(other); - - this.flags = pendingPermissionInfo.flags; - this.descriptionRes = pendingPermissionInfo.descriptionRes; - - this.backgroundPermission = pendingPermissionInfo.backgroundPermission; - this.group = pendingPermissionInfo.group; - this.requestRes = pendingPermissionInfo.requestRes; - this.protectionLevel = pendingPermissionInfo.protectionLevel; - - setName(name); - setPackageName(packageName); - } - public ParsedPermission setGroup(String group) { this.group = TextUtils.safeIntern(group); return this; diff --git a/core/java/android/content/pm/permission/SplitPermissionInfoParcelable.java b/core/java/android/content/pm/permission/SplitPermissionInfoParcelable.java index 421ae492ba6f..e7a554ae5726 100644 --- a/core/java/android/content/pm/permission/SplitPermissionInfoParcelable.java +++ b/core/java/android/content/pm/permission/SplitPermissionInfoParcelable.java @@ -18,6 +18,7 @@ package android.content.pm.permission; import android.annotation.IntRange; import android.annotation.NonNull; +import android.annotation.Nullable; import android.os.Parcel; import android.os.Parcelable; @@ -122,7 +123,7 @@ public class SplitPermissionInfoParcelable implements Parcelable { @Override @DataClass.Generated.Member - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { // You can override field equality logic by defining either of the methods like: // boolean fieldNameEquals(SplitPermissionInfoParcelable other) { ... } // boolean fieldNameEquals(FieldType otherValue) { ... } diff --git a/core/java/android/content/res/CompatibilityInfo.java b/core/java/android/content/res/CompatibilityInfo.java index c66c70a20729..ccb8cdd71278 100644 --- a/core/java/android/content/res/CompatibilityInfo.java +++ b/core/java/android/content/res/CompatibilityInfo.java @@ -16,6 +16,7 @@ package android.content.res; +import android.annotation.Nullable; import android.compat.annotation.UnsupportedAppUsage; import android.content.pm.ApplicationInfo; import android.graphics.Canvas; @@ -549,7 +550,7 @@ public class CompatibilityInfo implements Parcelable { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (this == o) { return true; } diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java index 49f81f83c39a..9f3634443d62 100644 --- a/core/java/android/content/res/Configuration.java +++ b/core/java/android/content/res/Configuration.java @@ -2128,7 +2128,7 @@ public final class Configuration implements Parcelable, Comparable<Configuration return this.compareTo(that) == 0; } - public boolean equals(Object that) { + public boolean equals(@Nullable Object that) { try { return equals((Configuration)that); } catch (ClassCastException e) { diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java index 0f1c876a1133..c1f0a5f778b9 100644 --- a/core/java/android/content/res/Resources.java +++ b/core/java/android/content/res/Resources.java @@ -1913,7 +1913,7 @@ public class Resources { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (this == o) { return true; } diff --git a/core/java/android/content/res/ResourcesKey.java b/core/java/android/content/res/ResourcesKey.java index fcb80aa3e58d..05769ddc5397 100644 --- a/core/java/android/content/res/ResourcesKey.java +++ b/core/java/android/content/res/ResourcesKey.java @@ -137,7 +137,7 @@ public final class ResourcesKey { } @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { if (!(obj instanceof ResourcesKey)) { return false; } diff --git a/core/java/android/content/rollback/PackageRollbackInfo.java b/core/java/android/content/rollback/PackageRollbackInfo.java index 642a76b453c9..0140280cd3d5 100644 --- a/core/java/android/content/rollback/PackageRollbackInfo.java +++ b/core/java/android/content/rollback/PackageRollbackInfo.java @@ -18,7 +18,6 @@ package android.content.rollback; import android.annotation.NonNull; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.content.pm.PackageManager; import android.content.pm.VersionedPackage; import android.os.Parcel; @@ -32,7 +31,7 @@ import java.util.List; * * @hide */ -@SystemApi @TestApi +@SystemApi public final class PackageRollbackInfo implements Parcelable { private final VersionedPackage mVersionRolledBackFrom; diff --git a/core/java/android/content/rollback/RollbackInfo.java b/core/java/android/content/rollback/RollbackInfo.java index c09cfd54866c..a363718a8b1d 100644 --- a/core/java/android/content/rollback/RollbackInfo.java +++ b/core/java/android/content/rollback/RollbackInfo.java @@ -18,7 +18,6 @@ package android.content.rollback; import android.annotation.NonNull; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.content.pm.VersionedPackage; import android.os.Parcel; import android.os.Parcelable; @@ -31,7 +30,7 @@ import java.util.List; * * @hide */ -@SystemApi @TestApi +@SystemApi public final class RollbackInfo implements Parcelable { /** diff --git a/core/java/android/content/rollback/RollbackManager.java b/core/java/android/content/rollback/RollbackManager.java index 7ebeb212b64a..3636222f558c 100644 --- a/core/java/android/content/rollback/RollbackManager.java +++ b/core/java/android/content/rollback/RollbackManager.java @@ -24,7 +24,6 @@ import android.annotation.SystemService; import android.annotation.TestApi; import android.content.Context; import android.content.IntentSender; -import android.content.pm.PackageInstaller; import android.content.pm.ParceledListSlice; import android.content.pm.VersionedPackage; import android.os.RemoteException; @@ -43,7 +42,7 @@ import java.util.List; * @see PackageInstaller.SessionParams#setEnableRollback(boolean) * @hide */ -@SystemApi @TestApi +@SystemApi @SystemService(Context.ROLLBACK_SERVICE) public final class RollbackManager { private final String mCallerPackageName; diff --git a/core/java/android/database/Cursor.java b/core/java/android/database/Cursor.java index 2afb755031b9..afa1c209c811 100644 --- a/core/java/android/database/Cursor.java +++ b/core/java/android/database/Cursor.java @@ -16,6 +16,8 @@ package android.database; +import android.annotation.IntDef; +import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.ContentResolver; @@ -23,6 +25,8 @@ import android.net.Uri; import android.os.Bundle; import java.io.Closeable; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.Arrays; import java.util.List; @@ -56,12 +60,23 @@ public interface Cursor extends Closeable { /** Value returned by {@link #getType(int)} if the specified column type is blob */ static final int FIELD_TYPE_BLOB = 4; + /** @hide */ + @IntDef(prefix = { "FIELD_TYPE_" }, value = { + FIELD_TYPE_NULL, + FIELD_TYPE_INTEGER, + FIELD_TYPE_FLOAT, + FIELD_TYPE_STRING, + FIELD_TYPE_BLOB, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface FieldType {} + /** * Returns the numbers of rows in the cursor. * * @return the number of rows in the cursor. */ - int getCount(); + @IntRange(from = 0) int getCount(); /** * Returns the current position of the cursor in the row set. @@ -72,7 +87,7 @@ public interface Cursor extends Closeable { * * @return the current cursor position. */ - int getPosition(); + @IntRange(from = -1) int getPosition(); /** * Move the cursor by a relative amount, forward or backward, from the @@ -101,7 +116,7 @@ public interface Cursor extends Closeable { * @param position the zero-based position to move to. * @return whether the requested move fully succeeded. */ - boolean moveToPosition(int position); + boolean moveToPosition(@IntRange(from = -1) int position); /** * Move the cursor to the first row. @@ -181,7 +196,7 @@ public interface Cursor extends Closeable { * the column name does not exist. * @see #getColumnIndexOrThrow(String) */ - int getColumnIndex(String columnName); + @IntRange(from = -1) int getColumnIndex(String columnName); /** * Returns the zero-based index for the given column name, or throws @@ -194,7 +209,8 @@ public interface Cursor extends Closeable { * @see #getColumnIndex(String) * @throws IllegalArgumentException if the column does not exist */ - int getColumnIndexOrThrow(String columnName) throws IllegalArgumentException; + @IntRange(from = 0) int getColumnIndexOrThrow(String columnName) + throws IllegalArgumentException; /** * Returns the column name at the given zero-based column index. @@ -202,7 +218,7 @@ public interface Cursor extends Closeable { * @param columnIndex the zero-based index of the target column. * @return the column name for the given column index. */ - String getColumnName(int columnIndex); + String getColumnName(@IntRange(from = 0) int columnIndex); /** * Returns a string array holding the names of all of the columns in the @@ -216,7 +232,7 @@ public interface Cursor extends Closeable { * Return total number of columns * @return number of columns */ - int getColumnCount(); + @IntRange(from = 0) int getColumnCount(); /** * Returns the value of the requested column as a byte array. @@ -228,7 +244,7 @@ public interface Cursor extends Closeable { * @param columnIndex the zero-based index of the target column. * @return the value of that column as a byte array. */ - byte[] getBlob(int columnIndex); + byte[] getBlob(@IntRange(from = 0) int columnIndex); /** * Returns the value of the requested column as a String. @@ -240,7 +256,7 @@ public interface Cursor extends Closeable { * @param columnIndex the zero-based index of the target column. * @return the value of that column as a String. */ - String getString(int columnIndex); + String getString(@IntRange(from = 0) int columnIndex); /** * Retrieves the requested column text and stores it in the buffer provided. @@ -250,7 +266,7 @@ public interface Cursor extends Closeable { * if the target column is null, return buffer * @param buffer the buffer to copy the text into. */ - void copyStringToBuffer(int columnIndex, CharArrayBuffer buffer); + void copyStringToBuffer(@IntRange(from = 0) int columnIndex, CharArrayBuffer buffer); /** * Returns the value of the requested column as a short. @@ -263,7 +279,7 @@ public interface Cursor extends Closeable { * @param columnIndex the zero-based index of the target column. * @return the value of that column as a short. */ - short getShort(int columnIndex); + short getShort(@IntRange(from = 0) int columnIndex); /** * Returns the value of the requested column as an int. @@ -276,7 +292,7 @@ public interface Cursor extends Closeable { * @param columnIndex the zero-based index of the target column. * @return the value of that column as an int. */ - int getInt(int columnIndex); + int getInt(@IntRange(from = 0) int columnIndex); /** * Returns the value of the requested column as a long. @@ -289,7 +305,7 @@ public interface Cursor extends Closeable { * @param columnIndex the zero-based index of the target column. * @return the value of that column as a long. */ - long getLong(int columnIndex); + long getLong(@IntRange(from = 0) int columnIndex); /** * Returns the value of the requested column as a float. @@ -302,7 +318,7 @@ public interface Cursor extends Closeable { * @param columnIndex the zero-based index of the target column. * @return the value of that column as a float. */ - float getFloat(int columnIndex); + float getFloat(@IntRange(from = 0) int columnIndex); /** * Returns the value of the requested column as a double. @@ -315,28 +331,18 @@ public interface Cursor extends Closeable { * @param columnIndex the zero-based index of the target column. * @return the value of that column as a double. */ - double getDouble(int columnIndex); + double getDouble(@IntRange(from = 0) int columnIndex); /** * Returns data type of the given column's value. * The preferred type of the column is returned but the data may be converted to other types * as documented in the get-type methods such as {@link #getInt(int)}, {@link #getFloat(int)} * etc. - *<p> - * Returned column types are - * <ul> - * <li>{@link #FIELD_TYPE_NULL}</li> - * <li>{@link #FIELD_TYPE_INTEGER}</li> - * <li>{@link #FIELD_TYPE_FLOAT}</li> - * <li>{@link #FIELD_TYPE_STRING}</li> - * <li>{@link #FIELD_TYPE_BLOB}</li> - *</ul> - *</p> * * @param columnIndex the zero-based index of the target column. * @return column value type */ - int getType(int columnIndex); + @FieldType int getType(@IntRange(from = 0) int columnIndex); /** * Returns <code>true</code> if the value in the indicated column is null. @@ -344,7 +350,7 @@ public interface Cursor extends Closeable { * @param columnIndex the zero-based index of the target column. * @return whether the column value is null. */ - boolean isNull(int columnIndex); + boolean isNull(@IntRange(from = 0) int columnIndex); /** * Deactivates the Cursor, making all calls on it fail until {@link #requery} is called. diff --git a/core/java/android/database/CursorWindow.java b/core/java/android/database/CursorWindow.java index 063a2d00a306..ac0593869d5a 100644 --- a/core/java/android/database/CursorWindow.java +++ b/core/java/android/database/CursorWindow.java @@ -17,6 +17,7 @@ package android.database; import android.annotation.BytesLong; +import android.annotation.IntRange; import android.compat.annotation.UnsupportedAppUsage; import android.content.res.Resources; import android.database.sqlite.SQLiteClosable; @@ -230,7 +231,7 @@ public class CursorWindow extends SQLiteClosable implements Parcelable { * * @return The zero-based start position. */ - public int getStartPosition() { + public @IntRange(from = 0) int getStartPosition() { return mStartPos; } @@ -243,7 +244,7 @@ public class CursorWindow extends SQLiteClosable implements Parcelable { * * @param pos The new zero-based start position. */ - public void setStartPosition(int pos) { + public void setStartPosition(@IntRange(from = 0) int pos) { mStartPos = pos; } @@ -252,7 +253,7 @@ public class CursorWindow extends SQLiteClosable implements Parcelable { * * @return The number of rows in this cursor window. */ - public int getNumRows() { + public @IntRange(from = 0) int getNumRows() { acquireReference(); try { return nativeGetNumRows(mWindowPtr); @@ -272,7 +273,7 @@ public class CursorWindow extends SQLiteClosable implements Parcelable { * @param columnNum The new number of columns. * @return True if successful. */ - public boolean setNumColumns(int columnNum) { + public boolean setNumColumns(@IntRange(from = 0) int columnNum) { acquireReference(); try { return nativeSetNumColumns(mWindowPtr, columnNum); @@ -317,7 +318,7 @@ public class CursorWindow extends SQLiteClosable implements Parcelable { * @deprecated Use {@link #getType(int, int)} instead. */ @Deprecated - public boolean isNull(int row, int column) { + public boolean isNull(@IntRange(from = 0) int row, @IntRange(from = 0) int column) { return getType(row, column) == Cursor.FIELD_TYPE_NULL; } @@ -332,7 +333,7 @@ public class CursorWindow extends SQLiteClosable implements Parcelable { * @deprecated Use {@link #getType(int, int)} instead. */ @Deprecated - public boolean isBlob(int row, int column) { + public boolean isBlob(@IntRange(from = 0) int row, @IntRange(from = 0) int column) { int type = getType(row, column); return type == Cursor.FIELD_TYPE_BLOB || type == Cursor.FIELD_TYPE_NULL; } @@ -347,7 +348,7 @@ public class CursorWindow extends SQLiteClosable implements Parcelable { * @deprecated Use {@link #getType(int, int)} instead. */ @Deprecated - public boolean isLong(int row, int column) { + public boolean isLong(@IntRange(from = 0) int row, @IntRange(from = 0) int column) { return getType(row, column) == Cursor.FIELD_TYPE_INTEGER; } @@ -361,7 +362,7 @@ public class CursorWindow extends SQLiteClosable implements Parcelable { * @deprecated Use {@link #getType(int, int)} instead. */ @Deprecated - public boolean isFloat(int row, int column) { + public boolean isFloat(@IntRange(from = 0) int row, @IntRange(from = 0) int column) { return getType(row, column) == Cursor.FIELD_TYPE_FLOAT; } @@ -376,29 +377,20 @@ public class CursorWindow extends SQLiteClosable implements Parcelable { * @deprecated Use {@link #getType(int, int)} instead. */ @Deprecated - public boolean isString(int row, int column) { + public boolean isString(@IntRange(from = 0) int row, @IntRange(from = 0) int column) { int type = getType(row, column); return type == Cursor.FIELD_TYPE_STRING || type == Cursor.FIELD_TYPE_NULL; } /** * Returns the type of the field at the specified row and column index. - * <p> - * The returned field types are: - * <ul> - * <li>{@link Cursor#FIELD_TYPE_NULL}</li> - * <li>{@link Cursor#FIELD_TYPE_INTEGER}</li> - * <li>{@link Cursor#FIELD_TYPE_FLOAT}</li> - * <li>{@link Cursor#FIELD_TYPE_STRING}</li> - * <li>{@link Cursor#FIELD_TYPE_BLOB}</li> - * </ul> - * </p> * * @param row The zero-based row index. * @param column The zero-based column index. * @return The field type. */ - public int getType(int row, int column) { + public @Cursor.FieldType int getType(@IntRange(from = 0) int row, + @IntRange(from = 0) int column) { acquireReference(); try { return nativeGetType(mWindowPtr, row - mStartPos, column); @@ -428,7 +420,7 @@ public class CursorWindow extends SQLiteClosable implements Parcelable { * @param column The zero-based column index. * @return The value of the field as a byte array. */ - public byte[] getBlob(int row, int column) { + public byte[] getBlob(@IntRange(from = 0) int row, @IntRange(from = 0) int column) { acquireReference(); try { return nativeGetBlob(mWindowPtr, row - mStartPos, column); @@ -463,7 +455,7 @@ public class CursorWindow extends SQLiteClosable implements Parcelable { * @param column The zero-based column index. * @return The value of the field as a string. */ - public String getString(int row, int column) { + public String getString(@IntRange(from = 0) int row, @IntRange(from = 0) int column) { acquireReference(); try { return nativeGetString(mWindowPtr, row - mStartPos, column); @@ -502,7 +494,8 @@ public class CursorWindow extends SQLiteClosable implements Parcelable { * @param buffer The {@link CharArrayBuffer} to hold the string. It is automatically * resized if the requested string is larger than the buffer's current capacity. */ - public void copyStringToBuffer(int row, int column, CharArrayBuffer buffer) { + public void copyStringToBuffer(@IntRange(from = 0) int row, @IntRange(from = 0) int column, + CharArrayBuffer buffer) { if (buffer == null) { throw new IllegalArgumentException("CharArrayBuffer should not be null"); } @@ -536,7 +529,7 @@ public class CursorWindow extends SQLiteClosable implements Parcelable { * @param column The zero-based column index. * @return The value of the field as a <code>long</code>. */ - public long getLong(int row, int column) { + public long getLong(@IntRange(from = 0) int row, @IntRange(from = 0) int column) { acquireReference(); try { return nativeGetLong(mWindowPtr, row - mStartPos, column); @@ -568,7 +561,7 @@ public class CursorWindow extends SQLiteClosable implements Parcelable { * @param column The zero-based column index. * @return The value of the field as a <code>double</code>. */ - public double getDouble(int row, int column) { + public double getDouble(@IntRange(from = 0) int row, @IntRange(from = 0) int column) { acquireReference(); try { return nativeGetDouble(mWindowPtr, row - mStartPos, column); @@ -589,7 +582,7 @@ public class CursorWindow extends SQLiteClosable implements Parcelable { * @param column The zero-based column index. * @return The value of the field as a <code>short</code>. */ - public short getShort(int row, int column) { + public short getShort(@IntRange(from = 0) int row, @IntRange(from = 0) int column) { return (short) getLong(row, column); } @@ -605,7 +598,7 @@ public class CursorWindow extends SQLiteClosable implements Parcelable { * @param column The zero-based column index. * @return The value of the field as an <code>int</code>. */ - public int getInt(int row, int column) { + public int getInt(@IntRange(from = 0) int row, @IntRange(from = 0) int column) { return (int) getLong(row, column); } @@ -621,7 +614,7 @@ public class CursorWindow extends SQLiteClosable implements Parcelable { * @param column The zero-based column index. * @return The value of the field as an <code>float</code>. */ - public float getFloat(int row, int column) { + public float getFloat(@IntRange(from = 0) int row, @IntRange(from = 0) int column) { return (float) getDouble(row, column); } @@ -633,7 +626,8 @@ public class CursorWindow extends SQLiteClosable implements Parcelable { * @param column The zero-based column index. * @return True if successful. */ - public boolean putBlob(byte[] value, int row, int column) { + public boolean putBlob(byte[] value, + @IntRange(from = 0) int row, @IntRange(from = 0) int column) { acquireReference(); try { return nativePutBlob(mWindowPtr, value, row - mStartPos, column); @@ -650,7 +644,8 @@ public class CursorWindow extends SQLiteClosable implements Parcelable { * @param column The zero-based column index. * @return True if successful. */ - public boolean putString(String value, int row, int column) { + public boolean putString(String value, + @IntRange(from = 0) int row, @IntRange(from = 0) int column) { acquireReference(); try { return nativePutString(mWindowPtr, value, row - mStartPos, column); @@ -667,7 +662,8 @@ public class CursorWindow extends SQLiteClosable implements Parcelable { * @param column The zero-based column index. * @return True if successful. */ - public boolean putLong(long value, int row, int column) { + public boolean putLong(long value, + @IntRange(from = 0) int row, @IntRange(from = 0) int column) { acquireReference(); try { return nativePutLong(mWindowPtr, value, row - mStartPos, column); @@ -685,7 +681,8 @@ public class CursorWindow extends SQLiteClosable implements Parcelable { * @param column The zero-based column index. * @return True if successful. */ - public boolean putDouble(double value, int row, int column) { + public boolean putDouble(double value, + @IntRange(from = 0) int row, @IntRange(from = 0) int column) { acquireReference(); try { return nativePutDouble(mWindowPtr, value, row - mStartPos, column); @@ -701,7 +698,7 @@ public class CursorWindow extends SQLiteClosable implements Parcelable { * @param column The zero-based column index. * @return True if successful. */ - public boolean putNull(int row, int column) { + public boolean putNull(@IntRange(from = 0) int row, @IntRange(from = 0) int column) { acquireReference(); try { return nativePutNull(mWindowPtr, row - mStartPos, column); diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java index 215990568fbf..86031dd918f5 100644 --- a/core/java/android/hardware/Camera.java +++ b/core/java/android/hardware/Camera.java @@ -2151,7 +2151,7 @@ public class Camera { * same as those of this size. {@code false} otherwise. */ @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { if (!(obj instanceof Size)) { return false; } @@ -2222,7 +2222,7 @@ public class Camera { * the same as those of this area. {@code false} otherwise. */ @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { if (!(obj instanceof Area)) { return false; } diff --git a/core/java/android/hardware/Sensor.java b/core/java/android/hardware/Sensor.java index a6e8c1395701..0f3cdfca70f5 100644 --- a/core/java/android/hardware/Sensor.java +++ b/core/java/android/hardware/Sensor.java @@ -616,7 +616,7 @@ public final class Sensor { public static final String STRING_TYPE_MOTION_DETECT = "android.sensor.motion_detect"; /** - * A constant describing a motion detect sensor. + * A constant describing a heart beat sensor. * * See {@link android.hardware.SensorEvent#values SensorEvent.values} for more details. * diff --git a/core/java/android/hardware/SensorEvent.java b/core/java/android/hardware/SensorEvent.java index 9906331be497..236fab0275cf 100644 --- a/core/java/android/hardware/SensorEvent.java +++ b/core/java/android/hardware/SensorEvent.java @@ -550,7 +550,7 @@ public class SensorEvent { * <h4>{@link android.hardware.Sensor#TYPE_HEART_BEAT * Sensor.TYPE_HEART_BEAT}:</h4> * - * A sensor of this type returns an event everytime a hear beat peak is + * A sensor of this type returns an event everytime a heart beat peak is * detected. * * Peak here ideally corresponds to the positive peak in the QRS complex of diff --git a/core/java/android/hardware/biometrics/BiometricConstants.java b/core/java/android/hardware/biometrics/BiometricConstants.java index 3040932f69dd..bed9a0640693 100644 --- a/core/java/android/hardware/biometrics/BiometricConstants.java +++ b/core/java/android/hardware/biometrics/BiometricConstants.java @@ -172,8 +172,7 @@ public interface BiometricConstants { BIOMETRIC_ERROR_NEGATIVE_BUTTON, BIOMETRIC_ERROR_NO_DEVICE_CREDENTIAL, BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED, - BIOMETRIC_PAUSED_REJECTED, - BIOMETRIC_ERROR_VENDOR_BASE}) + BIOMETRIC_PAUSED_REJECTED}) @Retention(RetentionPolicy.SOURCE) @interface Errors {} @@ -182,6 +181,19 @@ public interface BiometricConstants { // /** + * @hide + */ + @IntDef({BIOMETRIC_ACQUIRED_GOOD, + BIOMETRIC_ACQUIRED_PARTIAL, + BIOMETRIC_ACQUIRED_INSUFFICIENT, + BIOMETRIC_ACQUIRED_IMAGER_DIRTY, + BIOMETRIC_ACQUIRED_TOO_SLOW, + BIOMETRIC_ACQUIRED_TOO_FAST, + BIOMETRIC_ACQUIRED_VENDOR}) + @Retention(RetentionPolicy.SOURCE) + @interface Acquired {} + + /** * The image acquired was good. */ int BIOMETRIC_ACQUIRED_GOOD = 0; diff --git a/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java b/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java index 46e8cc036809..c7b554b3aafc 100644 --- a/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java +++ b/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java @@ -16,11 +16,15 @@ package android.hardware.biometrics; +import android.annotation.IntDef; import android.app.KeyguardManager; import android.compat.annotation.UnsupportedAppUsage; import android.hardware.biometrics.BiometricManager.Authenticators; import android.hardware.fingerprint.FingerprintManager; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + /** * Interface containing all of the fingerprint-specific constants. * @@ -36,6 +40,27 @@ public interface BiometricFingerprintConstants { // /** + * @hide + */ + @IntDef({FINGERPRINT_ERROR_HW_UNAVAILABLE, + FINGERPRINT_ERROR_UNABLE_TO_PROCESS, + FINGERPRINT_ERROR_TIMEOUT, + FINGERPRINT_ERROR_NO_SPACE, + FINGERPRINT_ERROR_CANCELED, + FINGERPRINT_ERROR_UNABLE_TO_REMOVE, + FINGERPRINT_ERROR_LOCKOUT, + FINGERPRINT_ERROR_VENDOR, + FINGERPRINT_ERROR_LOCKOUT_PERMANENT, + FINGERPRINT_ERROR_USER_CANCELED, + FINGERPRINT_ERROR_NO_FINGERPRINTS, + FINGERPRINT_ERROR_HW_NOT_PRESENT, + FINGERPRINT_ERROR_NEGATIVE_BUTTON, + BIOMETRIC_ERROR_NO_DEVICE_CREDENTIAL, + BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED}) + @Retention(RetentionPolicy.SOURCE) + @interface FingerprintError {} + + /** * The hardware is unavailable. Try again later. */ int FINGERPRINT_ERROR_HW_UNAVAILABLE = 1; @@ -150,6 +175,20 @@ public interface BiometricFingerprintConstants { // /** + * @hide + */ + @IntDef({FINGERPRINT_ACQUIRED_GOOD, + FINGERPRINT_ACQUIRED_PARTIAL, + FINGERPRINT_ACQUIRED_INSUFFICIENT, + FINGERPRINT_ACQUIRED_IMAGER_DIRTY, + FINGERPRINT_ACQUIRED_TOO_SLOW, + FINGERPRINT_ACQUIRED_TOO_FAST, + FINGERPRINT_ACQUIRED_VENDOR, + FINGERPRINT_ACQUIRED_START}) + @Retention(RetentionPolicy.SOURCE) + @interface FingerprintAcquired {} + + /** * The image acquired was good. */ int FINGERPRINT_ACQUIRED_GOOD = 0; diff --git a/core/java/android/hardware/biometrics/BiometricManager.java b/core/java/android/hardware/biometrics/BiometricManager.java index 35ef53b52891..25c749b9c14b 100644 --- a/core/java/android/hardware/biometrics/BiometricManager.java +++ b/core/java/android/hardware/biometrics/BiometricManager.java @@ -35,6 +35,8 @@ import android.util.Slog; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.ArrayList; +import java.util.List; /** * A class that contains biometric utilities. For authentication, see {@link BiometricPrompt}. @@ -199,13 +201,24 @@ public class BiometricManager { } /** + * @return A list of {@link SensorProperties} + * @hide + */ + @TestApi + @NonNull + @RequiresPermission(TEST_BIOMETRIC) + public List<SensorProperties> getSensorProperties() { + return new ArrayList<>(); // TODO(169459906) + } + + /** * Retrieves a test session for BiometricManager/BiometricPrompt. * @hide */ @TestApi @NonNull @RequiresPermission(TEST_BIOMETRIC) - public BiometricTestSession getTestSession() { + public BiometricTestSession createTestSession(int sensorId) { return null; // TODO(169459906) } diff --git a/core/java/android/hardware/biometrics/BiometricTestSession.java b/core/java/android/hardware/biometrics/BiometricTestSession.java index 719efa8ce6dd..802655b0d364 100644 --- a/core/java/android/hardware/biometrics/BiometricTestSession.java +++ b/core/java/android/hardware/biometrics/BiometricTestSession.java @@ -22,11 +22,9 @@ import android.annotation.NonNull; import android.annotation.RequiresPermission; import android.annotation.TestApi; import android.content.Context; +import android.hardware.fingerprint.FingerprintManager; import android.os.RemoteException; -import android.util.Log; - -import java.util.ArrayList; -import java.util.List; +import android.util.ArraySet; /** * Common set of interfaces to test biometric-related APIs, including {@link BiometricPrompt} and @@ -35,37 +33,20 @@ import java.util.List; */ @TestApi public class BiometricTestSession implements AutoCloseable { - - private static final String TAG = "TestManager"; - private final Context mContext; - private final ITestService mTestService; + private final ITestSession mTestSession; + + // Keep track of users that were tested, which need to be cleaned up when finishing. + private final ArraySet<Integer> mTestedUsers; /** * @hide */ - public BiometricTestSession(@NonNull Context context, @NonNull ITestService testService) { + public BiometricTestSession(@NonNull Context context, @NonNull ITestSession testSession) { mContext = context; - mTestService = testService; - } - - /** - * @return A list of {@link SensorProperties} - */ - @NonNull - @RequiresPermission(TEST_BIOMETRIC) - public List<SensorProperties> getSensorProperties() { - try { - final List<SensorPropertiesInternal> internalProps = - mTestService.getSensorPropertiesInternal(mContext.getOpPackageName()); - final List<SensorProperties> props = new ArrayList<>(); - for (SensorPropertiesInternal internalProp : internalProps) { - props.add(new SensorProperties(internalProp.sensorId, internalProp.sensorStrength)); - } - return props; - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } + mTestSession = testSession; + mTestedUsers = new ArraySet<>(); + enableTestHal(true); } /** @@ -75,105 +56,106 @@ public class BiometricTestSession implements AutoCloseable { * secure pathways such as HAT/Keystore are not testable, since they depend on the TEE or its * equivalent for the secret key. * - * @param sensorId Sensor that this command applies to. * @param enableTestHal If true, enable testing with a fake HAL instead of the real HAL. */ @RequiresPermission(TEST_BIOMETRIC) - public void enableTestHal(int sensorId, boolean enableTestHal) { + private void enableTestHal(boolean enableTestHal) { try { - mTestService.enableTestHal(sensorId, enableTestHal); + mTestSession.enableTestHal(enableTestHal); } catch (RemoteException e) { - Log.e(TAG, "Remote exception", e); + throw e.rethrowFromSystemServer(); } } /** * Starts the enrollment process. This should generally be used when the test HAL is enabled. * - * @param sensorId Sensor that this command applies to. * @param userId User that this command applies to. */ @RequiresPermission(TEST_BIOMETRIC) - public void enrollStart(int sensorId, int userId) { + public void startEnroll(int userId) { try { - mTestService.enrollStart(sensorId, userId); + mTestedUsers.add(userId); + mTestSession.startEnroll(userId); } catch (RemoteException e) { - Log.e(TAG, "Remote exception", e); + throw e.rethrowFromSystemServer(); } } /** * Finishes the enrollment process. Simulates the HAL's callback. * - * @param sensorId Sensor that this command applies to. * @param userId User that this command applies to. */ @RequiresPermission(TEST_BIOMETRIC) - public void enrollFinish(int sensorId, int userId) { + public void finishEnroll(int userId) { try { - mTestService.enrollFinish(sensorId, userId); + mTestedUsers.add(userId); + mTestSession.finishEnroll(userId); } catch (RemoteException e) { - Log.e(TAG, "Remote exception", e); + throw e.rethrowFromSystemServer(); } } /** * Simulates a successful authentication, but does not provide a valid HAT. * - * @param sensorId Sensor that this command applies to. * @param userId User that this command applies to. */ @RequiresPermission(TEST_BIOMETRIC) - public void authenticateSuccess(int sensorId, int userId) { + public void acceptAuthentication(int userId) { try { - mTestService.authenticateSuccess(sensorId, userId); + mTestSession.acceptAuthentication(userId); } catch (RemoteException e) { - Log.e(TAG, "Remote exception", e); + throw e.rethrowFromSystemServer(); } } /** * Simulates a rejected attempt. * - * @param sensorId Sensor that this command applies to. * @param userId User that this command applies to. */ @RequiresPermission(TEST_BIOMETRIC) - public void authenticateReject(int sensorId, int userId) { + public void rejectAuthentication(int userId) { try { - mTestService.authenticateReject(sensorId, userId); + mTestSession.rejectAuthentication(userId); } catch (RemoteException e) { - Log.e(TAG, "Remote exception", e); + throw e.rethrowFromSystemServer(); } } /** * Simulates an acquired message from the HAL. * - * @param sensorId Sensor that this command applies to. * @param userId User that this command applies to. + * @param acquireInfo See + * {@link BiometricPrompt.AuthenticationCallback#onAuthenticationAcquired(int)} and + * {@link FingerprintManager.AuthenticationCallback#onAuthenticationAcquired(int)} */ @RequiresPermission(TEST_BIOMETRIC) - public void notifyAcquired(int sensorId, int userId) { + public void notifyAcquired(int userId, int acquireInfo) { try { - mTestService.notifyAcquired(sensorId, userId); + mTestSession.notifyAcquired(userId, acquireInfo); } catch (RemoteException e) { - Log.e(TAG, "Remote exception", e); + throw e.rethrowFromSystemServer(); } } /** * Simulates an error message from the HAL. * - * @param sensorId Sensor that this command applies to. * @param userId User that this command applies to. + * @param errorCode See + * {@link BiometricPrompt.AuthenticationCallback#onAuthenticationError(int, CharSequence)} and + * {@link FingerprintManager.AuthenticationCallback#onAuthenticationError(int, CharSequence)} */ @RequiresPermission(TEST_BIOMETRIC) - public void notifyError(int sensorId, int userId) { + public void notifyError(int userId, int errorCode) { try { - mTestService.notifyError(sensorId, userId); + mTestSession.notifyError(userId, errorCode); } catch (RemoteException e) { - Log.e(TAG, "Remote exception", e); + throw e.rethrowFromSystemServer(); } } @@ -182,21 +164,24 @@ public class BiometricTestSession implements AutoCloseable { * that isn't known by both sides are deleted. This should generally be used when the test * HAL is disabled (e.g. to clean up after a test). * - * @param sensorId Sensor that this command applies to. * @param userId User that this command applies to. */ @RequiresPermission(TEST_BIOMETRIC) - public void internalCleanup(int sensorId, int userId) { + public void cleanupInternalState(int userId) { try { - mTestService.internalCleanup(sensorId, userId); + mTestSession.cleanupInternalState(userId); } catch (RemoteException e) { - Log.e(TAG, "Remote exception", e); + throw e.rethrowFromSystemServer(); } } @Override @RequiresPermission(TEST_BIOMETRIC) public void close() { + for (int user : mTestedUsers) { + cleanupInternalState(user); + } + enableTestHal(false); } } diff --git a/core/java/android/hardware/biometrics/ITestService.aidl b/core/java/android/hardware/biometrics/ITestSession.aidl index 637313266022..6112f17949d7 100644 --- a/core/java/android/hardware/biometrics/ITestService.aidl +++ b/core/java/android/hardware/biometrics/ITestSession.aidl @@ -21,37 +21,34 @@ import android.hardware.biometrics.SensorPropertiesInternal; * A test service for FingerprintManager and BiometricPrompt. * @hide */ -interface ITestService { - // Returns a list of sensor properties supported by the interface. - List<SensorPropertiesInternal> getSensorPropertiesInternal(String opPackageName); - +interface ITestSession { // Switches the specified sensor to use a test HAL. In this mode, the framework will not invoke // any methods on the real HAL implementation. This allows the framework to test a substantial // portion of the framework code that would otherwise require human interaction. Note that // secure pathways such as HAT/Keystore are not testable, since they depend on the TEE or its // equivalent for the secret key. - void enableTestHal(int sensorId, boolean enableTestHal); + void enableTestHal(boolean enableTestHal); // Starts the enrollment process. This should generally be used when the test HAL is enabled. - void enrollStart(int sensorId, int userId); + void startEnroll(int userId); // Finishes the enrollment process. Simulates the HAL's callback. - void enrollFinish(int sensorId, int userId); + void finishEnroll(int userId); // Simulates a successful authentication, but does not provide a valid HAT. - void authenticateSuccess(int sensorId, int userId); + void acceptAuthentication(int userId); // Simulates a rejected attempt. - void authenticateReject(int sensorId, int userId); + void rejectAuthentication(int userId); // Simulates an acquired message from the HAL. - void notifyAcquired(int sensorId, int userId); + void notifyAcquired(int userId, int acquireInfo); // Simulates an error message from the HAL. - void notifyError(int sensorId, int userId); + void notifyError(int userId, int errorCode); // Matches the framework's cached enrollments against the HAL's enrollments. Any enrollment // that isn't known by both sides are deleted. This should generally be used when the test // HAL is disabled (e.g. to clean up after a test). - void internalCleanup(int sensorId, int userId); + void cleanupInternalState(int userId); } diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java index decf05396c1f..cd137078818c 100644 --- a/core/java/android/hardware/camera2/CameraDevice.java +++ b/core/java/android/hardware/camera2/CameraDevice.java @@ -20,7 +20,6 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.hardware.camera2.params.InputConfiguration; import android.hardware.camera2.params.OutputConfiguration; import android.hardware.camera2.params.SessionConfiguration; @@ -358,7 +357,6 @@ public abstract class CameraDevice implements AutoCloseable { * @hide */ @SystemApi - @TestApi public static final int SESSION_OPERATION_MODE_NORMAL = 0; // ICameraDeviceUser.NORMAL_MODE; @@ -369,7 +367,6 @@ public abstract class CameraDevice implements AutoCloseable { * @hide */ @SystemApi - @TestApi public static final int SESSION_OPERATION_MODE_CONSTRAINED_HIGH_SPEED = 1; // ICameraDeviceUser.CONSTRAINED_HIGH_SPEED_MODE; @@ -380,7 +377,6 @@ public abstract class CameraDevice implements AutoCloseable { * @hide */ @SystemApi - @TestApi public static final int SESSION_OPERATION_MODE_VENDOR_START = 0x8000; // ICameraDeviceUser.VENDOR_MODE_START; @@ -423,7 +419,6 @@ public abstract class CameraDevice implements AutoCloseable { * @hide */ @SystemApi - @TestApi @Deprecated public abstract void createCustomCaptureSession( InputConfiguration inputConfig, diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java index 0d0bfb32e293..47dafa0c61a8 100644 --- a/core/java/android/hardware/camera2/CaptureRequest.java +++ b/core/java/android/hardware/camera2/CaptureRequest.java @@ -528,7 +528,7 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> * @return True if the requests are the same, false otherwise. */ @Override - public boolean equals(Object other) { + public boolean equals(@Nullable Object other) { return other instanceof CaptureRequest && equals((CaptureRequest)other); } diff --git a/core/java/android/hardware/camera2/marshal/MarshalRegistry.java b/core/java/android/hardware/camera2/marshal/MarshalRegistry.java index 15650879942a..0575f37c9cf8 100644 --- a/core/java/android/hardware/camera2/marshal/MarshalRegistry.java +++ b/core/java/android/hardware/camera2/marshal/MarshalRegistry.java @@ -15,6 +15,7 @@ */ package android.hardware.camera2.marshal; +import android.annotation.Nullable; import android.hardware.camera2.impl.CameraMetadataNative; import android.hardware.camera2.utils.TypeReference; @@ -114,7 +115,7 @@ public class MarshalRegistry { private final int hash; @Override - public boolean equals(Object other) { + public boolean equals(@Nullable Object other) { if (other instanceof MarshalToken<?>) { MarshalToken<?> otherToken = (MarshalToken<?>)other; return typeReference.equals(otherToken.typeReference) && diff --git a/core/java/android/hardware/camera2/params/BlackLevelPattern.java b/core/java/android/hardware/camera2/params/BlackLevelPattern.java index c3dc25fb1beb..4405df8c8a2b 100644 --- a/core/java/android/hardware/camera2/params/BlackLevelPattern.java +++ b/core/java/android/hardware/camera2/params/BlackLevelPattern.java @@ -16,6 +16,8 @@ package android.hardware.camera2.params; +import android.annotation.Nullable; + import java.util.Arrays; import java.util.Objects; @@ -107,7 +109,7 @@ public final class BlackLevelPattern { * @return {@code true} if the objects were equal, {@code false} otherwise */ @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { if (obj == null) { return false; } else if (this == obj) { diff --git a/core/java/android/hardware/camera2/params/InputConfiguration.java b/core/java/android/hardware/camera2/params/InputConfiguration.java index d95f889b3212..0a50f974aca8 100644 --- a/core/java/android/hardware/camera2/params/InputConfiguration.java +++ b/core/java/android/hardware/camera2/params/InputConfiguration.java @@ -16,6 +16,7 @@ package android.hardware.camera2.params; +import android.annotation.Nullable; import android.hardware.camera2.utils.HashCodeHelpers; /** @@ -90,7 +91,7 @@ public final class InputConfiguration { * @return {@code true} if the objects were equal, {@code false} otherwise. */ @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { if (!(obj instanceof InputConfiguration)) { return false; } diff --git a/core/java/android/hardware/camera2/params/OutputConfiguration.java b/core/java/android/hardware/camera2/params/OutputConfiguration.java index 226b8e549a9e..a20a1bf194ea 100644 --- a/core/java/android/hardware/camera2/params/OutputConfiguration.java +++ b/core/java/android/hardware/camera2/params/OutputConfiguration.java @@ -17,6 +17,8 @@ package android.hardware.camera2.params; +import static com.android.internal.util.Preconditions.*; + import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; @@ -31,8 +33,6 @@ import android.util.Log; import android.util.Size; import android.view.Surface; -import static com.android.internal.util.Preconditions.*; - import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -677,7 +677,7 @@ public final class OutputConfiguration implements Parcelable { * @return {@code true} if the objects were equal, {@code false} otherwise */ @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { if (obj == null) { return false; } else if (this == obj) { diff --git a/core/java/android/hardware/camera2/params/SessionConfiguration.java b/core/java/android/hardware/camera2/params/SessionConfiguration.java index 47a897cb2c55..8fc919f142a2 100644 --- a/core/java/android/hardware/camera2/params/SessionConfiguration.java +++ b/core/java/android/hardware/camera2/params/SessionConfiguration.java @@ -22,6 +22,7 @@ import static com.android.internal.util.Preconditions.*; import android.annotation.CallbackExecutor; import android.annotation.IntDef; import android.annotation.NonNull; +import android.annotation.Nullable; import android.hardware.camera2.CameraCaptureSession; import android.hardware.camera2.CameraCharacteristics; import android.hardware.camera2.CameraDevice; @@ -190,7 +191,7 @@ public final class SessionConfiguration implements Parcelable { * @return {@code true} if the objects were equal, {@code false} otherwise */ @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { if (obj == null) { return false; } else if (this == obj) { diff --git a/core/java/android/hardware/camera2/params/TonemapCurve.java b/core/java/android/hardware/camera2/params/TonemapCurve.java index 90e63556f018..a11d2c71528f 100644 --- a/core/java/android/hardware/camera2/params/TonemapCurve.java +++ b/core/java/android/hardware/camera2/params/TonemapCurve.java @@ -18,6 +18,7 @@ package android.hardware.camera2.params; import static com.android.internal.util.Preconditions.*; +import android.annotation.Nullable; import android.graphics.PointF; import android.hardware.camera2.CameraCharacteristics; import android.hardware.camera2.CameraDevice; @@ -249,7 +250,7 @@ public final class TonemapCurve { * @return {@code true} if the objects were equal, {@code false} otherwise */ @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { if (obj == null) { return false; } diff --git a/core/java/android/hardware/camera2/utils/TypeReference.java b/core/java/android/hardware/camera2/utils/TypeReference.java index 435ed153e042..3c1d20663426 100644 --- a/core/java/android/hardware/camera2/utils/TypeReference.java +++ b/core/java/android/hardware/camera2/utils/TypeReference.java @@ -18,6 +18,7 @@ package android.hardware.camera2.utils; import static com.android.internal.util.Preconditions.checkNotNull; +import android.annotation.Nullable; import android.compat.annotation.UnsupportedAppUsage; import java.lang.reflect.Array; @@ -244,7 +245,7 @@ public abstract class TypeReference<T> { * is also equal.</p> */ @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { // Note that this comparison could inaccurately return true when comparing types // with nested type variables; therefore we ban type variables in the constructor. return o instanceof TypeReference<?> && mType.equals(((TypeReference<?>)o).mType); diff --git a/core/java/android/hardware/devicestate/DeviceStateManager.java b/core/java/android/hardware/devicestate/DeviceStateManager.java new file mode 100644 index 000000000000..a52f983ff2a7 --- /dev/null +++ b/core/java/android/hardware/devicestate/DeviceStateManager.java @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.devicestate; + +import android.annotation.SystemService; +import android.content.Context; + +/** + * Manages the state of the system for devices with user-configurable hardware like a foldable + * phone. + * + * @hide + */ +@SystemService(Context.DEVICE_STATE_SERVICE) +public final class DeviceStateManager { + /** Invalid device state. */ + public static final int INVALID_DEVICE_STATE = -1; + + private DeviceStateManagerGlobal mGlobal; + + public DeviceStateManager() { + mGlobal = DeviceStateManagerGlobal.getInstance(); + } +} diff --git a/core/java/android/hardware/devicestate/DeviceStateManagerGlobal.java b/core/java/android/hardware/devicestate/DeviceStateManagerGlobal.java new file mode 100644 index 000000000000..4e7cf4af118d --- /dev/null +++ b/core/java/android/hardware/devicestate/DeviceStateManagerGlobal.java @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.devicestate; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.Context; +import android.os.IBinder; +import android.os.ServiceManager; + +/** + * Provides communication with the device state system service on behalf of applications. + * + * @see DeviceStateManager + * @hide + */ +final class DeviceStateManagerGlobal { + private static DeviceStateManagerGlobal sInstance; + + /** + * Returns an instance of {@link DeviceStateManagerGlobal}. May return {@code null} if a + * connection with the device state service couldn't be established. + */ + @Nullable + static DeviceStateManagerGlobal getInstance() { + synchronized (DeviceStateManagerGlobal.class) { + if (sInstance == null) { + IBinder b = ServiceManager.getService(Context.DEVICE_STATE_SERVICE); + if (b != null) { + sInstance = new DeviceStateManagerGlobal(IDeviceStateManager + .Stub.asInterface(b)); + } + } + return sInstance; + } + } + + @NonNull + private final IDeviceStateManager mDeviceStateManager; + + private DeviceStateManagerGlobal(@NonNull IDeviceStateManager deviceStateManager) { + mDeviceStateManager = deviceStateManager; + } +} diff --git a/core/java/android/hardware/devicestate/IDeviceStateManager.aidl b/core/java/android/hardware/devicestate/IDeviceStateManager.aidl new file mode 100644 index 000000000000..24913e9b2d23 --- /dev/null +++ b/core/java/android/hardware/devicestate/IDeviceStateManager.aidl @@ -0,0 +1,20 @@ +/** + * Copyright (c) 2020, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.devicestate; + +/** @hide */ +interface IDeviceStateManager {} diff --git a/core/java/android/hardware/display/AmbientBrightnessDayStats.java b/core/java/android/hardware/display/AmbientBrightnessDayStats.java index 26fd2654239f..8aff911ea42c 100644 --- a/core/java/android/hardware/display/AmbientBrightnessDayStats.java +++ b/core/java/android/hardware/display/AmbientBrightnessDayStats.java @@ -19,7 +19,6 @@ package android.hardware.display; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.os.Parcel; import android.os.Parcelable; @@ -36,7 +35,6 @@ import java.util.Objects; * @hide */ @SystemApi -@TestApi public final class AmbientBrightnessDayStats implements Parcelable { /** The localdate for which brightness stats are being tracked */ diff --git a/core/java/android/hardware/display/BrightnessChangeEvent.java b/core/java/android/hardware/display/BrightnessChangeEvent.java index a6a44bea816b..e2d836c59099 100644 --- a/core/java/android/hardware/display/BrightnessChangeEvent.java +++ b/core/java/android/hardware/display/BrightnessChangeEvent.java @@ -19,7 +19,6 @@ package android.hardware.display; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.os.Parcel; import android.os.Parcelable; @@ -32,7 +31,6 @@ import java.util.Objects; * @hide */ @SystemApi -@TestApi public final class BrightnessChangeEvent implements Parcelable { /** Brightness in nits */ public final float brightness; diff --git a/core/java/android/hardware/display/BrightnessConfiguration.java b/core/java/android/hardware/display/BrightnessConfiguration.java index 6412a0ce7219..d9c1063cd39d 100644 --- a/core/java/android/hardware/display/BrightnessConfiguration.java +++ b/core/java/android/hardware/display/BrightnessConfiguration.java @@ -20,7 +20,6 @@ import android.annotation.FloatRange; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.content.pm.ApplicationInfo; import android.os.Parcel; import android.os.Parcelable; @@ -44,7 +43,6 @@ import java.util.Objects; /** @hide */ @SystemApi -@TestApi public final class BrightnessConfiguration implements Parcelable { private static final String TAG_BRIGHTNESS_CURVE = "brightness-curve"; private static final String TAG_BRIGHTNESS_POINT = "brightness-point"; diff --git a/core/java/android/hardware/display/BrightnessCorrection.java b/core/java/android/hardware/display/BrightnessCorrection.java index 22df778a1368..bbfc45edb89a 100644 --- a/core/java/android/hardware/display/BrightnessCorrection.java +++ b/core/java/android/hardware/display/BrightnessCorrection.java @@ -20,7 +20,6 @@ import android.annotation.FloatRange; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.os.Parcel; import android.os.Parcelable; import android.util.MathUtils; @@ -44,7 +43,6 @@ import java.io.IOException; * @hide */ @SystemApi -@TestApi public final class BrightnessCorrection implements Parcelable { private static final int SCALE_AND_TRANSLATE_LOG = 1; @@ -238,7 +236,7 @@ public final class BrightnessCorrection implements Parcelable { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (o == this) { return true; } diff --git a/core/java/android/hardware/display/DeviceProductInfo.java b/core/java/android/hardware/display/DeviceProductInfo.java index f8d36758d1b1..41126b70c89f 100644 --- a/core/java/android/hardware/display/DeviceProductInfo.java +++ b/core/java/android/hardware/display/DeviceProductInfo.java @@ -16,6 +16,7 @@ package android.hardware.display; +import android.annotation.Nullable; import android.os.Parcel; import android.os.Parcelable; @@ -124,7 +125,7 @@ public final class DeviceProductInfo implements Parcelable { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; DeviceProductInfo that = (DeviceProductInfo) o; @@ -227,7 +228,7 @@ public final class DeviceProductInfo implements Parcelable { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; ManufactureDate that = (ManufactureDate) o; diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java index fc14b89d83f7..68b9d5227746 100644 --- a/core/java/android/hardware/display/DisplayManager.java +++ b/core/java/android/hardware/display/DisplayManager.java @@ -699,7 +699,6 @@ public final class DisplayManager { * @hide */ @SystemApi - @TestApi public Point getStableDisplaySize() { return mGlobal.getStableDisplaySize(); } @@ -709,7 +708,6 @@ public final class DisplayManager { * @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()); @@ -721,7 +719,6 @@ public final class DisplayManager { * @hide until we make it a system api */ @SystemApi - @TestApi @RequiresPermission(Manifest.permission.ACCESS_AMBIENT_LIGHT_STATS) public List<AmbientBrightnessDayStats> getAmbientBrightnessStats() { return mGlobal.getAmbientBrightnessStats(); @@ -733,7 +730,6 @@ public final class DisplayManager { * @hide */ @SystemApi - @TestApi @RequiresPermission(Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS) public void setBrightnessConfiguration(BrightnessConfiguration c) { setBrightnessConfigurationForUser(c, mContext.getUserId(), mContext.getPackageName()); @@ -758,7 +754,6 @@ public final class DisplayManager { * @hide */ @SystemApi - @TestApi @RequiresPermission(Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS) public BrightnessConfiguration getBrightnessConfiguration() { return getBrightnessConfigurationForUser(mContext.getUserId()); @@ -784,7 +779,6 @@ public final class DisplayManager { * @hide */ @SystemApi - @TestApi @RequiresPermission(Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS) @Nullable public BrightnessConfiguration getDefaultBrightnessConfiguration() { diff --git a/core/java/android/hardware/display/DisplayManagerInternal.java b/core/java/android/hardware/display/DisplayManagerInternal.java index c7f89151624d..defcab7c3035 100644 --- a/core/java/android/hardware/display/DisplayManagerInternal.java +++ b/core/java/android/hardware/display/DisplayManagerInternal.java @@ -372,7 +372,7 @@ public abstract class DisplayManagerInternal { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { return o instanceof DisplayPowerRequest && equals((DisplayPowerRequest)o); } diff --git a/core/java/android/hardware/display/DisplayViewport.java b/core/java/android/hardware/display/DisplayViewport.java index 8dcfc948d968..d2b66c38af34 100644 --- a/core/java/android/hardware/display/DisplayViewport.java +++ b/core/java/android/hardware/display/DisplayViewport.java @@ -105,7 +105,7 @@ public final class DisplayViewport { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (o == this) { return true; } diff --git a/core/java/android/hardware/display/WifiDisplay.java b/core/java/android/hardware/display/WifiDisplay.java index 5bbbbf9bae77..39fd0a863fb3 100644 --- a/core/java/android/hardware/display/WifiDisplay.java +++ b/core/java/android/hardware/display/WifiDisplay.java @@ -16,6 +16,7 @@ package android.hardware.display; +import android.annotation.Nullable; import android.compat.annotation.UnsupportedAppUsage; import android.os.Parcel; import android.os.Parcelable; @@ -135,7 +136,7 @@ public final class WifiDisplay implements Parcelable { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { return o instanceof WifiDisplay && equals((WifiDisplay)o); } diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java index b35a68f64dc6..2aefb1da4783 100644 --- a/core/java/android/hardware/fingerprint/FingerprintManager.java +++ b/core/java/android/hardware/fingerprint/FingerprintManager.java @@ -39,6 +39,7 @@ import android.hardware.biometrics.BiometricFingerprintConstants; import android.hardware.biometrics.BiometricPrompt; import android.hardware.biometrics.BiometricTestSession; import android.hardware.biometrics.IBiometricServiceLockoutResetCallback; +import android.hardware.biometrics.SensorProperties; import android.os.Binder; import android.os.CancellationSignal; import android.os.CancellationSignal.OnCancelListener; @@ -97,6 +98,24 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing private Fingerprint mRemovalFingerprint; private Handler mHandler; + + /** + * Retrieves a list of properties for all fingerprint sensors on the device. + * @hide + */ + @TestApi + @NonNull + @RequiresPermission(TEST_BIOMETRIC) + public List<SensorProperties> getSensorProperties() { + final List<SensorProperties> properties = new ArrayList<>(); + final List<FingerprintSensorPropertiesInternal> internalProperties + = getSensorPropertiesInternal(); + for (FingerprintSensorPropertiesInternal internalProp : internalProperties) { + properties.add(FingerprintSensorProperties.from(internalProp)); + } + return properties; + } + /** * Retrieves a test session for FingerprintManager. * @hide @@ -104,10 +123,10 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing @TestApi @NonNull @RequiresPermission(TEST_BIOMETRIC) - public BiometricTestSession getTestSession() { + public BiometricTestSession createTestSession(int sensorId) { try { return new BiometricTestSession(mContext, - mService.getTestService(mContext.getOpPackageName())); + mService.createTestSession(sensorId, mContext.getOpPackageName())); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -607,10 +626,10 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing * @hide */ @RequiresPermission(MANAGE_FINGERPRINT) - public void generateChallenge(int sensorId, GenerateChallengeCallback callback) { + public void generateChallenge(int sensorId, int userId, GenerateChallengeCallback callback) { if (mService != null) try { mGenerateChallengeCallback = callback; - mService.generateChallenge(mToken, sensorId, mServiceReceiver, + mService.generateChallenge(mToken, sensorId, userId, mServiceReceiver, mContext.getOpPackageName()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); @@ -623,7 +642,7 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing * @hide */ @RequiresPermission(MANAGE_FINGERPRINT) - public void generateChallenge(GenerateChallengeCallback callback) { + public void generateChallenge(int userId, GenerateChallengeCallback callback) { final List<FingerprintSensorPropertiesInternal> fingerprintSensorProperties = getSensorPropertiesInternal(); if (fingerprintSensorProperties.isEmpty()) { @@ -632,7 +651,7 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing } final int sensorId = fingerprintSensorProperties.get(0).sensorId; - generateChallenge(sensorId, callback); + generateChallenge(sensorId, userId, callback); } /** @@ -640,10 +659,10 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing * @hide */ @RequiresPermission(MANAGE_FINGERPRINT) - public void revokeChallenge() { + public void revokeChallenge(int userId) { // On HALs with only single in-flight challenge such as IBiometricsFingerprint@2.1, // this parameter is ignored. - revokeChallenge(0L); + revokeChallenge(userId, 0L); } /** @@ -651,9 +670,17 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing * @hide */ @RequiresPermission(MANAGE_FINGERPRINT) - public void revokeChallenge(long challenge) { + public void revokeChallenge(int userId, long challenge) { if (mService != null) try { - mService.revokeChallenge(mToken, mContext.getOpPackageName(), challenge); + final List<FingerprintSensorPropertiesInternal> fingerprintSensorProperties = + getSensorPropertiesInternal(); + if (fingerprintSensorProperties.isEmpty()) { + Slog.e(TAG, "No sensors"); + return; + } + final int sensorId = fingerprintSensorProperties.get(0).sensorId; + mService.revokeChallenge(mToken, sensorId, userId, mContext.getOpPackageName(), + challenge); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -878,21 +905,6 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing } /** - * Retrieves a list of properties for all fingerprint sensors on the device. - * @hide - */ - @NonNull - public List<FingerprintSensorProperties> getSensorProperties() { - final List<FingerprintSensorProperties> properties = new ArrayList<>(); - final List<FingerprintSensorPropertiesInternal> internalProperties - = getSensorPropertiesInternal(); - for (FingerprintSensorPropertiesInternal internalProp : internalProperties) { - properties.add(FingerprintSensorProperties.from(internalProp)); - } - return properties; - } - - /** * Get statically configured sensor properties. * @hide */ diff --git a/core/java/android/hardware/fingerprint/IFingerprintService.aidl b/core/java/android/hardware/fingerprint/IFingerprintService.aidl index e8ca590ebf1e..95668372c5a1 100644 --- a/core/java/android/hardware/fingerprint/IFingerprintService.aidl +++ b/core/java/android/hardware/fingerprint/IFingerprintService.aidl @@ -17,7 +17,7 @@ package android.hardware.fingerprint; import android.hardware.biometrics.IBiometricSensorReceiver; import android.hardware.biometrics.IBiometricServiceLockoutResetCallback; -import android.hardware.biometrics.ITestService; +import android.hardware.biometrics.ITestSession; import android.hardware.fingerprint.IFingerprintClientActiveCallback; import android.hardware.fingerprint.IFingerprintServiceReceiver; import android.hardware.fingerprint.IUdfpsOverlayController; @@ -32,8 +32,8 @@ import java.util.List; */ interface IFingerprintService { - // Retrieves a test service - ITestService getTestService(String opPackageName); + // Creates a test session with the specified sensorId + ITestSession createTestSession(int sensorId, String opPackageName); // Retrieve static sensor properties for all fingerprint sensors List<FingerprintSensorPropertiesInternal> getSensorPropertiesInternal(String opPackageName); @@ -93,10 +93,10 @@ interface IFingerprintService { boolean isHardwareDetected(String opPackageName); // Get a pre-enrollment authentication token - void generateChallenge(IBinder token, int sensorId, IFingerprintServiceReceiver receiver, String opPackageName); + void generateChallenge(IBinder token, int sensorId, int userId, IFingerprintServiceReceiver receiver, String opPackageName); // Finish an enrollment sequence and invalidate the authentication token - void revokeChallenge(IBinder token, String opPackageName, long challenge); + void revokeChallenge(IBinder token, int sensorId, int userId, String opPackageName, long challenge); // Determine if a user has at least one enrolled fingerprint boolean hasEnrolledFingerprints(int userId, String opPackageName); diff --git a/core/java/android/hardware/hdmi/HdmiControlManager.java b/core/java/android/hardware/hdmi/HdmiControlManager.java index e5e73200c8ef..5b186c78df54 100644 --- a/core/java/android/hardware/hdmi/HdmiControlManager.java +++ b/core/java/android/hardware/hdmi/HdmiControlManager.java @@ -28,7 +28,6 @@ import android.annotation.StringDef; import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.annotation.SystemService; -import android.annotation.TestApi; import android.content.Context; import android.content.pm.PackageManager; import android.os.Binder; @@ -60,7 +59,6 @@ import java.util.concurrent.Executor; * @hide */ @SystemApi -@TestApi @SystemService(Context.HDMI_CONTROL_SERVICE) @RequiresFeature(PackageManager.FEATURE_HDMI_CEC) public final class HdmiControlManager { diff --git a/core/java/android/hardware/hdmi/HdmiPortInfo.java b/core/java/android/hardware/hdmi/HdmiPortInfo.java index 52c3628f358b..e97e120109bf 100644 --- a/core/java/android/hardware/hdmi/HdmiPortInfo.java +++ b/core/java/android/hardware/hdmi/HdmiPortInfo.java @@ -18,7 +18,6 @@ package android.hardware.hdmi; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.os.Parcel; import android.os.Parcelable; @@ -29,7 +28,6 @@ import android.os.Parcelable; * @hide */ @SystemApi -@TestApi public final class HdmiPortInfo implements Parcelable { /** HDMI port type: Input */ public static final int PORT_INPUT = 0; diff --git a/core/java/android/hardware/hdmi/HdmiSwitchClient.java b/core/java/android/hardware/hdmi/HdmiSwitchClient.java index 4685e1e04618..cbfbe3940e47 100644 --- a/core/java/android/hardware/hdmi/HdmiSwitchClient.java +++ b/core/java/android/hardware/hdmi/HdmiSwitchClient.java @@ -18,7 +18,6 @@ package android.hardware.hdmi; import android.annotation.CallbackExecutor; import android.annotation.NonNull; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.hardware.hdmi.HdmiControlManager.ControlCallbackResult; import android.os.Binder; import android.os.RemoteException; @@ -39,7 +38,6 @@ import java.util.concurrent.Executor; * @hide */ @SystemApi -@TestApi public class HdmiSwitchClient extends HdmiClient { private static final String TAG = "HdmiSwitchClient"; diff --git a/core/java/android/hardware/input/InputDeviceIdentifier.java b/core/java/android/hardware/input/InputDeviceIdentifier.java index 26f2393ab03d..c673e7ab7c53 100644 --- a/core/java/android/hardware/input/InputDeviceIdentifier.java +++ b/core/java/android/hardware/input/InputDeviceIdentifier.java @@ -16,12 +16,13 @@ package android.hardware.input; -import java.util.Objects; - +import android.annotation.Nullable; import android.os.Parcel; import android.os.Parcelable; import android.text.TextUtils; +import java.util.Objects; + /** * Wrapper for passing identifying information for input devices. * @@ -69,7 +70,7 @@ public final class InputDeviceIdentifier implements Parcelable { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (this == o) return true; if (o == null || !(o instanceof InputDeviceIdentifier)) return false; diff --git a/core/java/android/hardware/input/TouchCalibration.java b/core/java/android/hardware/input/TouchCalibration.java index 7a6eba294f73..cec7cfcadf6f 100644 --- a/core/java/android/hardware/input/TouchCalibration.java +++ b/core/java/android/hardware/input/TouchCalibration.java @@ -16,6 +16,7 @@ package android.hardware.input; +import android.annotation.Nullable; import android.os.Parcel; import android.os.Parcelable; @@ -97,7 +98,7 @@ public class TouchCalibration implements Parcelable { } @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { if (obj == this) { return true; } else if (obj instanceof TouchCalibration) { diff --git a/core/java/android/hardware/lights/Light.java b/core/java/android/hardware/lights/Light.java index e90b57cdc7b4..da270182052d 100644 --- a/core/java/android/hardware/lights/Light.java +++ b/core/java/android/hardware/lights/Light.java @@ -17,8 +17,8 @@ package android.hardware.lights; import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.os.Parcel; import android.os.Parcelable; @@ -28,7 +28,6 @@ import android.os.Parcelable; * @hide */ @SystemApi -@TestApi public final class Light implements Parcelable { private final int mId; private final int mOrdinal; @@ -78,7 +77,7 @@ public final class Light implements Parcelable { }; @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { if (obj instanceof Light) { Light light = (Light) obj; return mId == light.mId && mOrdinal == light.mOrdinal && mType == light.mType; diff --git a/core/java/android/hardware/lights/LightState.java b/core/java/android/hardware/lights/LightState.java index e55aa702f15c..cd39e6df91a9 100644 --- a/core/java/android/hardware/lights/LightState.java +++ b/core/java/android/hardware/lights/LightState.java @@ -19,7 +19,6 @@ package android.hardware.lights; import android.annotation.ColorInt; import android.annotation.NonNull; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.os.Parcel; import android.os.Parcelable; @@ -36,7 +35,6 @@ import android.os.Parcelable; * @hide */ @SystemApi -@TestApi public final class LightState implements Parcelable { private final int mColor; diff --git a/core/java/android/hardware/lights/LightsManager.java b/core/java/android/hardware/lights/LightsManager.java index 8cd231224472..33e5fcaf2abb 100644 --- a/core/java/android/hardware/lights/LightsManager.java +++ b/core/java/android/hardware/lights/LightsManager.java @@ -45,7 +45,6 @@ import java.util.List; * @hide */ @SystemApi -@TestApi @SystemService(Context.LIGHTS_SERVICE) public final class LightsManager { private static final String TAG = "LightsManager"; diff --git a/core/java/android/hardware/lights/LightsRequest.java b/core/java/android/hardware/lights/LightsRequest.java index 5c4fc6707e96..a318992c35ee 100644 --- a/core/java/android/hardware/lights/LightsRequest.java +++ b/core/java/android/hardware/lights/LightsRequest.java @@ -18,7 +18,6 @@ package android.hardware.lights; import android.annotation.NonNull; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.util.SparseArray; import com.android.internal.util.Preconditions; @@ -29,7 +28,6 @@ import com.android.internal.util.Preconditions; * @hide */ @SystemApi -@TestApi public final class LightsRequest { /** Visible to {@link LightsManager.Session}. */ diff --git a/core/java/android/hardware/location/GeofenceHardwareImpl.java b/core/java/android/hardware/location/GeofenceHardwareImpl.java index ee2d43ce45f3..3715a61ef7d5 100644 --- a/core/java/android/hardware/location/GeofenceHardwareImpl.java +++ b/core/java/android/hardware/location/GeofenceHardwareImpl.java @@ -16,6 +16,7 @@ package android.hardware.location; +import android.annotation.Nullable; import android.content.Context; import android.content.pm.PackageManager; import android.location.IFusedGeofenceHardware; @@ -878,7 +879,7 @@ public final class GeofenceHardwareImpl { } @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { if (obj == null) return false; if (obj == this) return true; diff --git a/core/java/android/hardware/radio/ProgramList.java b/core/java/android/hardware/radio/ProgramList.java index ec318b749a5d..3a042a5dee4d 100644 --- a/core/java/android/hardware/radio/ProgramList.java +++ b/core/java/android/hardware/radio/ProgramList.java @@ -466,7 +466,7 @@ public final class ProgramList implements AutoCloseable { } @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { if (this == obj) return true; if (!(obj instanceof Chunk)) return false; Chunk other = (Chunk) obj; diff --git a/core/java/android/hardware/soundtrigger/SoundTrigger.java b/core/java/android/hardware/soundtrigger/SoundTrigger.java index 0f1c2a59965b..e58403fe387d 100644 --- a/core/java/android/hardware/soundtrigger/SoundTrigger.java +++ b/core/java/android/hardware/soundtrigger/SoundTrigger.java @@ -67,7 +67,6 @@ import java.util.UUID; * * @hide */ -@TestApi @SystemApi public class SoundTrigger { private static final String TAG = "SoundTrigger"; @@ -522,7 +521,7 @@ public class SoundTrigger { } @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { if (this == obj) { return true; } @@ -699,7 +698,7 @@ public class SoundTrigger { } @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { if (this == obj) { return true; } @@ -840,7 +839,7 @@ public class SoundTrigger { } @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { if (this == obj) { return true; } @@ -1527,7 +1526,7 @@ public class SoundTrigger { } @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { if (this == obj) return true; if (obj == null) @@ -1630,7 +1629,7 @@ public class SoundTrigger { } @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { if (this == obj) return true; if (obj == null) @@ -1752,7 +1751,7 @@ public class SoundTrigger { } @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { if (this == obj) return true; if (!super.equals(obj)) @@ -1819,7 +1818,7 @@ public class SoundTrigger { } @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { if (this == obj) return true; if (obj == null) @@ -1909,7 +1908,7 @@ public class SoundTrigger { } @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { if (this == obj) return true; if (obj == null) diff --git a/core/java/android/hardware/usb/AccessoryFilter.java b/core/java/android/hardware/usb/AccessoryFilter.java index f22dad4124d2..8345ff3f5342 100644 --- a/core/java/android/hardware/usb/AccessoryFilter.java +++ b/core/java/android/hardware/usb/AccessoryFilter.java @@ -17,6 +17,7 @@ package android.hardware.usb; import android.annotation.NonNull; +import android.annotation.Nullable; import android.service.usb.UsbAccessoryFilterProto; import com.android.internal.util.dump.DualDumpOutputStream; @@ -120,7 +121,7 @@ public class AccessoryFilter { } @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { // can't compare if we have wildcard strings if (mManufacturer == null || mModel == null || mVersion == null) { return false; diff --git a/core/java/android/hardware/usb/DeviceFilter.java b/core/java/android/hardware/usb/DeviceFilter.java index da979c0c3fb1..66b0a426f35d 100644 --- a/core/java/android/hardware/usb/DeviceFilter.java +++ b/core/java/android/hardware/usb/DeviceFilter.java @@ -17,6 +17,7 @@ package android.hardware.usb; import android.annotation.NonNull; +import android.annotation.Nullable; import android.service.usb.UsbDeviceFilterProto; import android.util.Slog; @@ -238,7 +239,7 @@ public class DeviceFilter { } @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { // can't compare if we have wildcard strings if (mVendorId == -1 || mProductId == -1 || mClass == -1 || mSubclass == -1 || mProtocol == -1) { diff --git a/core/java/android/hardware/usb/UsbAccessory.java b/core/java/android/hardware/usb/UsbAccessory.java index f4cfc74aa77d..47c6978e9e58 100644 --- a/core/java/android/hardware/usb/UsbAccessory.java +++ b/core/java/android/hardware/usb/UsbAccessory.java @@ -186,7 +186,7 @@ public class UsbAccessory implements Parcelable { } @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { if (obj instanceof UsbAccessory) { UsbAccessory accessory = (UsbAccessory)obj; return (compare(mManufacturer, accessory.getManufacturer()) && diff --git a/core/java/android/hardware/usb/UsbDevice.java b/core/java/android/hardware/usb/UsbDevice.java index a0f64cddfc74..71074df382fd 100644 --- a/core/java/android/hardware/usb/UsbDevice.java +++ b/core/java/android/hardware/usb/UsbDevice.java @@ -308,7 +308,7 @@ public class UsbDevice implements Parcelable { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (o instanceof UsbDevice) { return ((UsbDevice)o).mName.equals(mName); } else if (o instanceof String) { diff --git a/core/java/android/metrics/LogMaker.java b/core/java/android/metrics/LogMaker.java index d8a2082f4eae..a19eb5668d55 100644 --- a/core/java/android/metrics/LogMaker.java +++ b/core/java/android/metrics/LogMaker.java @@ -16,7 +16,6 @@ package android.metrics; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.content.ComponentName; import android.util.Log; import android.util.SparseArray; @@ -32,7 +31,6 @@ import com.android.internal.logging.nano.MetricsProto.MetricsEvent; * @hide */ @SystemApi -@TestApi public class LogMaker { private static final String TAG = "LogBuilder"; diff --git a/core/java/android/metrics/MetricsReader.java b/core/java/android/metrics/MetricsReader.java index 27f9a5dbf51c..5f356ca00d88 100644 --- a/core/java/android/metrics/MetricsReader.java +++ b/core/java/android/metrics/MetricsReader.java @@ -16,7 +16,6 @@ package android.metrics; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.util.EventLog; import com.android.internal.annotations.VisibleForTesting; @@ -36,7 +35,6 @@ import java.util.concurrent.TimeUnit; * @hide */ @SystemApi -@TestApi public class MetricsReader { private Queue<LogMaker> mPendingQueue = new LinkedList<>(); private Queue<LogMaker> mSeenQueue = new LinkedList<>(); diff --git a/core/java/android/net/CaptivePortal.java b/core/java/android/net/CaptivePortal.java index 8afeb3033cdb..c2586fa0c825 100644 --- a/core/java/android/net/CaptivePortal.java +++ b/core/java/android/net/CaptivePortal.java @@ -19,7 +19,6 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.RequiresPermission; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.os.IBinder; import android.os.Parcel; import android.os.Parcelable; @@ -42,7 +41,6 @@ public class CaptivePortal implements Parcelable { * @hide */ @SystemApi - @TestApi public static final int APP_RETURN_DISMISSED = 0; /** * Response code from the captive portal application, indicating that the user did not login and @@ -52,7 +50,6 @@ public class CaptivePortal implements Parcelable { * @hide */ @SystemApi - @TestApi public static final int APP_RETURN_UNWANTED = 1; /** * Response code from the captive portal application, indicating that the user does not wish to @@ -62,7 +59,6 @@ public class CaptivePortal implements Parcelable { * @hide */ @SystemApi - @TestApi public static final int APP_RETURN_WANTED_AS_IS = 2; /** Event offset of request codes from captive portal application. */ private static final int APP_REQUEST_BASE = 100; @@ -74,7 +70,6 @@ public class CaptivePortal implements Parcelable { * @hide */ @SystemApi - @TestApi public static final int APP_REQUEST_REEVALUATION_REQUIRED = APP_REQUEST_BASE + 0; private final IBinder mBinder; @@ -154,7 +149,6 @@ public class CaptivePortal implements Parcelable { * @hide */ @SystemApi - @TestApi public void useNetwork() { try { ICaptivePortal.Stub.asInterface(mBinder).appResponse(APP_RETURN_WANTED_AS_IS); @@ -167,7 +161,6 @@ public class CaptivePortal implements Parcelable { * @hide */ @SystemApi - @TestApi @RequiresPermission(android.Manifest.permission.NETWORK_STACK) public void reevaluateNetwork() { try { @@ -183,7 +176,6 @@ public class CaptivePortal implements Parcelable { * @hide */ @SystemApi - @TestApi public void logEvent(@EventId int eventId, @NonNull String packageName) { try { ICaptivePortal.Stub.asInterface(mBinder).logEvent(eventId, packageName); diff --git a/core/java/android/net/CaptivePortalData.java b/core/java/android/net/CaptivePortalData.java index 1357803a6cb8..59e62a675abc 100644 --- a/core/java/android/net/CaptivePortalData.java +++ b/core/java/android/net/CaptivePortalData.java @@ -19,7 +19,6 @@ package android.net; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.os.Parcel; import android.os.Parcelable; @@ -30,7 +29,6 @@ import java.util.Objects; * @hide */ @SystemApi -@TestApi public final class CaptivePortalData implements Parcelable { private final long mRefreshTimeMillis; @Nullable @@ -254,7 +252,7 @@ public final class CaptivePortalData implements Parcelable { } @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { if (!(obj instanceof CaptivePortalData)) return false; final CaptivePortalData other = (CaptivePortalData) obj; return mRefreshTimeMillis == other.mRefreshTimeMillis diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java index 5d4003afa753..1012f47f8c0a 100644 --- a/core/java/android/net/ConnectivityManager.java +++ b/core/java/android/net/ConnectivityManager.java @@ -269,7 +269,6 @@ public class ConnectivityManager { * {@hide} */ @SystemApi - @TestApi public static final String EXTRA_CAPTIVE_PORTAL_PROBE_SPEC = "android.net.extra.CAPTIVE_PORTAL_PROBE_SPEC"; @@ -278,7 +277,6 @@ public class ConnectivityManager { * {@hide} */ @SystemApi - @TestApi public static final String EXTRA_CAPTIVE_PORTAL_USER_AGENT = "android.net.extra.CAPTIVE_PORTAL_USER_AGENT"; @@ -899,6 +897,18 @@ public class ConnectivityManager { } /** + * @hide + * TODO: Expose for SystemServer when becomes a module. + */ + public void systemReady() { + try { + mService.systemReady(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** * Checks if a given type uses the cellular data connection. * This should be replaced in the future by a network property. * @param networkType the type to check @@ -4401,7 +4411,6 @@ public class ConnectivityManager { * @hide */ @SystemApi - @TestApi @RequiresPermission(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) public void startCaptivePortalApp(@NonNull Network network, @NonNull Bundle appExtras) { try { diff --git a/core/java/android/net/DataUsageRequest.java b/core/java/android/net/DataUsageRequest.java index 0ac8f7e794dc..b06d515b3acf 100644 --- a/core/java/android/net/DataUsageRequest.java +++ b/core/java/android/net/DataUsageRequest.java @@ -16,6 +16,7 @@ package android.net; +import android.annotation.Nullable; import android.net.NetworkTemplate; import android.os.Parcel; import android.os.Parcelable; @@ -95,7 +96,7 @@ public final class DataUsageRequest implements Parcelable { } @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { if (obj instanceof DataUsageRequest == false) return false; DataUsageRequest that = (DataUsageRequest) obj; return that.requestId == this.requestId diff --git a/core/java/android/net/DhcpResults.java b/core/java/android/net/DhcpResults.java index 1ef4f1741e3a..6819c349d20a 100644 --- a/core/java/android/net/DhcpResults.java +++ b/core/java/android/net/DhcpResults.java @@ -159,7 +159,7 @@ public final class DhcpResults implements Parcelable { } @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { if (this == obj) return true; if (!(obj instanceof DhcpResults)) return false; diff --git a/core/java/android/net/EthernetManager.java b/core/java/android/net/EthernetManager.java index d975017f9c8e..5860e20ad3b4 100644 --- a/core/java/android/net/EthernetManager.java +++ b/core/java/android/net/EthernetManager.java @@ -37,7 +37,6 @@ import java.util.concurrent.Executor; * @hide */ @SystemApi -@TestApi @SystemService(Context.ETHERNET_SERVICE) public class EthernetManager { private static final String TAG = "EthernetManager"; diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl index d7f178cd0a9b..059ec28298f9 100644 --- a/core/java/android/net/IConnectivityManager.aidl +++ b/core/java/android/net/IConnectivityManager.aidl @@ -233,4 +233,6 @@ interface IConnectivityManager void simulateDataStall(int detectionMethod, long timestampMillis, in Network network, in PersistableBundle extras); + + void systemReady(); } diff --git a/core/java/android/net/Ikev2VpnProfile.java b/core/java/android/net/Ikev2VpnProfile.java index c8ca618e5969..9876fc650f21 100644 --- a/core/java/android/net/Ikev2VpnProfile.java +++ b/core/java/android/net/Ikev2VpnProfile.java @@ -364,7 +364,7 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile { } @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { if (!(obj instanceof Ikev2VpnProfile)) { return false; } diff --git a/core/java/android/net/IpConfiguration.java b/core/java/android/net/IpConfiguration.java index 23d5ff7f3afe..fa31b806ba9f 100644 --- a/core/java/android/net/IpConfiguration.java +++ b/core/java/android/net/IpConfiguration.java @@ -166,7 +166,7 @@ public final class IpConfiguration implements Parcelable { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (o == this) { return true; } diff --git a/core/java/android/net/IpPrefix.java b/core/java/android/net/IpPrefix.java index 8cfe6df678c7..bcb65fab8571 100644 --- a/core/java/android/net/IpPrefix.java +++ b/core/java/android/net/IpPrefix.java @@ -18,8 +18,8 @@ package android.net; import android.annotation.IntRange; import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.os.Parcel; import android.os.Parcelable; import android.util.Pair; @@ -88,7 +88,6 @@ public final class IpPrefix implements Parcelable { * @hide */ @SystemApi - @TestApi public IpPrefix(@NonNull InetAddress address, @IntRange(from = 0, to = 128) int prefixLength) { // We don't reuse the (byte[], int) constructor because it calls clone() on the byte array, // which is unnecessary because getAddress() already returns a clone. @@ -107,7 +106,6 @@ public final class IpPrefix implements Parcelable { * @hide */ @SystemApi - @TestApi public IpPrefix(@NonNull String prefix) { // We don't reuse the (InetAddress, int) constructor because "error: call to this must be // first statement in constructor". We could factor out setting the member variables to an @@ -127,7 +125,7 @@ public final class IpPrefix implements Parcelable { * @return {@code true} if both objects are equal, {@code false} otherwise. */ @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { if (!(obj instanceof IpPrefix)) { return false; } diff --git a/core/java/android/net/IpSecAlgorithm.java b/core/java/android/net/IpSecAlgorithm.java index 38d9883f0003..e89451e4f4ef 100644 --- a/core/java/android/net/IpSecAlgorithm.java +++ b/core/java/android/net/IpSecAlgorithm.java @@ -17,6 +17,7 @@ package android.net; import android.annotation.NonNull; import android.annotation.StringDef; +import android.content.res.Resources; import android.os.Build; import android.os.Parcel; import android.os.Parcelable; @@ -27,6 +28,12 @@ import com.android.internal.util.HexDump; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; /** * This class represents a single algorithm that can be used by an {@link IpSecTransform}. @@ -52,6 +59,27 @@ public final class IpSecAlgorithm implements Parcelable { public static final String CRYPT_AES_CBC = "cbc(aes)"; /** + * AES-CTR Encryption/Ciphering Algorithm. + * + * <p>Valid lengths for keying material are {160, 224, 288}. + * + * <p>As per <a href="https://tools.ietf.org/html/rfc3686#section-5.1">RFC3686 (Section + * 5.1)</a>, keying material consists of a 128, 192, or 256 bit AES key followed by a 32-bit + * nonce. RFC compliance requires that the nonce must be unique per security association. + * + * <p>This algorithm may be available on the device. Caller MUST check if it is supported before + * using it by calling {@link #getSupportedAlgorithms()} and checking if this algorithm is + * included in the returned algorithm set. The returned algorithm set will not change unless the + * device is rebooted. {@link IllegalArgumentException} will be thrown if this algorithm is + * requested on an unsupported device. + * + * <p>@see {@link #getSupportedAlgorithms()} + */ + // This algorithm may be available on devices released before Android 12, and is guaranteed + // to be available on devices first shipped with Android 12 or later. + public static final String CRYPT_AES_CTR = "rfc3686(ctr(aes))"; + + /** * MD5 HMAC Authentication/Integrity Algorithm. <b>This algorithm is not recommended for use in * new applications and is provided for legacy compatibility with 3gpp infrastructure.</b> * @@ -99,6 +127,25 @@ public final class IpSecAlgorithm implements Parcelable { public static final String AUTH_HMAC_SHA512 = "hmac(sha512)"; /** + * AES-XCBC Authentication/Integrity Algorithm. + * + * <p>Keys for this algorithm must be 128 bits in length. + * + * <p>The only valid truncation length is 96 bits. + * + * <p>This algorithm may be available on the device. Caller MUST check if it is supported before + * using it by calling {@link #getSupportedAlgorithms()} and checking if this algorithm is + * included in the returned algorithm set. The returned algorithm set will not change unless the + * device is rebooted. {@link IllegalArgumentException} will be thrown if this algorithm is + * requested on an unsupported device. + * + * <p>@see {@link #getSupportedAlgorithms()} + */ + // This algorithm may be available on devices released before Android 12, and is guaranteed + // to be available on devices first shipped with Android 12 or later. + public static final String AUTH_AES_XCBC = "xcbc(aes)"; + + /** * AES-GCM Authentication/Integrity + Encryption/Ciphering Algorithm. * * <p>Valid lengths for keying material are {160, 224, 288}. @@ -111,19 +158,69 @@ public final class IpSecAlgorithm implements Parcelable { */ public static final String AUTH_CRYPT_AES_GCM = "rfc4106(gcm(aes))"; + /** + * ChaCha20-Poly1305 Authentication/Integrity + Encryption/Ciphering Algorithm. + * + * <p>Keys for this algorithm must be 288 bits in length. + * + * <p>As per <a href="https://tools.ietf.org/html/rfc7634#section-2">RFC7634 (Section 2)</a>, + * keying material consists of a 256 bit key followed by a 32-bit salt. The salt is fixed per + * security association. + * + * <p>The only valid ICV (truncation) length is 128 bits. + * + * <p>This algorithm may be available on the device. Caller MUST check if it is supported before + * using it by calling {@link #getSupportedAlgorithms()} and checking if this algorithm is + * included in the returned algorithm set. The returned algorithm set will not change unless the + * device is rebooted. {@link IllegalArgumentException} will be thrown if this algorithm is + * requested on an unsupported device. + * + * <p>@see {@link #getSupportedAlgorithms()} + */ + // This algorithm may be available on devices released before Android 12, and is guaranteed + // to be available on devices first shipped with Android 12 or later. + public static final String AUTH_CRYPT_CHACHA20_POLY1305 = "rfc7539esp(chacha20,poly1305)"; + /** @hide */ @StringDef({ CRYPT_AES_CBC, + CRYPT_AES_CTR, AUTH_HMAC_MD5, AUTH_HMAC_SHA1, AUTH_HMAC_SHA256, AUTH_HMAC_SHA384, AUTH_HMAC_SHA512, - AUTH_CRYPT_AES_GCM + AUTH_AES_XCBC, + AUTH_CRYPT_AES_GCM, + AUTH_CRYPT_CHACHA20_POLY1305 }) @Retention(RetentionPolicy.SOURCE) public @interface AlgorithmName {} + /** @hide */ + @VisibleForTesting + public static final Map<String, Integer> ALGO_TO_REQUIRED_FIRST_SDK = new HashMap<>(); + + private static final int SDK_VERSION_ZERO = 0; + + static { + ALGO_TO_REQUIRED_FIRST_SDK.put(CRYPT_AES_CBC, SDK_VERSION_ZERO); + ALGO_TO_REQUIRED_FIRST_SDK.put(AUTH_HMAC_MD5, SDK_VERSION_ZERO); + ALGO_TO_REQUIRED_FIRST_SDK.put(AUTH_HMAC_SHA1, SDK_VERSION_ZERO); + ALGO_TO_REQUIRED_FIRST_SDK.put(AUTH_HMAC_SHA256, SDK_VERSION_ZERO); + ALGO_TO_REQUIRED_FIRST_SDK.put(AUTH_HMAC_SHA384, SDK_VERSION_ZERO); + ALGO_TO_REQUIRED_FIRST_SDK.put(AUTH_HMAC_SHA512, SDK_VERSION_ZERO); + ALGO_TO_REQUIRED_FIRST_SDK.put(AUTH_CRYPT_AES_GCM, SDK_VERSION_ZERO); + + // STOPSHIP: b/170424293 Use Build.VERSION_CODES.S when it is defined + ALGO_TO_REQUIRED_FIRST_SDK.put(CRYPT_AES_CTR, Build.VERSION_CODES.R + 1); + ALGO_TO_REQUIRED_FIRST_SDK.put(AUTH_AES_XCBC, Build.VERSION_CODES.R + 1); + ALGO_TO_REQUIRED_FIRST_SDK.put(AUTH_CRYPT_CHACHA20_POLY1305, Build.VERSION_CODES.R + 1); + } + + private static final Set<String> ENABLED_ALGOS = + Collections.unmodifiableSet(loadAlgos(Resources.getSystem())); + private final String mName; private final byte[] mKey; private final int mTruncLenBits; @@ -137,6 +234,7 @@ public final class IpSecAlgorithm implements Parcelable { * * @param algorithm name of the algorithm. * @param key key padded to a multiple of 8 bits. + * @throws IllegalArgumentException if algorithm or key length is invalid. */ public IpSecAlgorithm(@NonNull @AlgorithmName String algorithm, @NonNull byte[] key) { this(algorithm, key, 0); @@ -152,6 +250,7 @@ public final class IpSecAlgorithm implements Parcelable { * @param algorithm name of the algorithm. * @param key key padded to a multiple of 8 bits. * @param truncLenBits number of bits of output hash to use. + * @throws IllegalArgumentException if algorithm, key length or truncation length is invalid. */ public IpSecAlgorithm( @NonNull @AlgorithmName String algorithm, @NonNull byte[] key, int truncLenBits) { @@ -206,13 +305,59 @@ public final class IpSecAlgorithm implements Parcelable { } }; + /** + * Returns supported IPsec algorithms for the current device. + * + * <p>Some algorithms may not be supported on old devices. Callers MUST check if an algorithm is + * supported before using it. + */ + @NonNull + public static Set<String> getSupportedAlgorithms() { + return ENABLED_ALGOS; + } + + /** @hide */ + @VisibleForTesting + public static Set<String> loadAlgos(Resources systemResources) { + final Set<String> enabledAlgos = new HashSet<>(); + + // Load and validate the optional algorithm resource. Undefined or duplicate algorithms in + // the resource are not allowed. + final String[] resourceAlgos = systemResources.getStringArray( + com.android.internal.R.array.config_optionalIpSecAlgorithms); + for (String str : resourceAlgos) { + if (!ALGO_TO_REQUIRED_FIRST_SDK.containsKey(str) || !enabledAlgos.add(str)) { + // This error should be caught by CTS and never be thrown to API callers + throw new IllegalArgumentException("Invalid or repeated algorithm " + str); + } + } + + for (Entry<String, Integer> entry : ALGO_TO_REQUIRED_FIRST_SDK.entrySet()) { + if (Build.VERSION.FIRST_SDK_INT >= entry.getValue()) { + enabledAlgos.add(entry.getKey()); + } + } + + return enabledAlgos; + } + private static void checkValidOrThrow(String name, int keyLen, int truncLen) { - boolean isValidLen = true; - boolean isValidTruncLen = true; + final boolean isValidLen; + final boolean isValidTruncLen; + + if (!getSupportedAlgorithms().contains(name)) { + throw new IllegalArgumentException("Unsupported algorithm: " + name); + } - switch(name) { + switch (name) { case CRYPT_AES_CBC: isValidLen = keyLen == 128 || keyLen == 192 || keyLen == 256; + isValidTruncLen = true; + break; + case CRYPT_AES_CTR: + // The keying material for AES-CTR is a key plus a 32-bit salt + isValidLen = keyLen == 128 + 32 || keyLen == 192 + 32 || keyLen == 256 + 32; + isValidTruncLen = true; break; case AUTH_HMAC_MD5: isValidLen = keyLen == 128; @@ -234,12 +379,22 @@ public final class IpSecAlgorithm implements Parcelable { isValidLen = keyLen == 512; isValidTruncLen = truncLen >= 256 && truncLen <= 512; break; + case AUTH_AES_XCBC: + isValidLen = keyLen == 128; + isValidTruncLen = truncLen == 96; + break; case AUTH_CRYPT_AES_GCM: // The keying material for GCM is a key plus a 32-bit salt isValidLen = keyLen == 128 + 32 || keyLen == 192 + 32 || keyLen == 256 + 32; isValidTruncLen = truncLen == 64 || truncLen == 96 || truncLen == 128; break; + case AUTH_CRYPT_CHACHA20_POLY1305: + // The keying material for ChaCha20Poly1305 is a key plus a 32-bit salt + isValidLen = keyLen == 256 + 32; + isValidTruncLen = truncLen == 128; + break; default: + // Should never hit here. throw new IllegalArgumentException("Couldn't find an algorithm: " + name); } @@ -260,6 +415,7 @@ public final class IpSecAlgorithm implements Parcelable { case AUTH_HMAC_SHA256: case AUTH_HMAC_SHA384: case AUTH_HMAC_SHA512: + case AUTH_AES_XCBC: return true; default: return false; @@ -268,12 +424,24 @@ public final class IpSecAlgorithm implements Parcelable { /** @hide */ public boolean isEncryption() { - return getName().equals(CRYPT_AES_CBC); + switch (getName()) { + case CRYPT_AES_CBC: // fallthrough + case CRYPT_AES_CTR: + return true; + default: + return false; + } } /** @hide */ public boolean isAead() { - return getName().equals(AUTH_CRYPT_AES_GCM); + switch (getName()) { + case AUTH_CRYPT_AES_GCM: // fallthrough + case AUTH_CRYPT_CHACHA20_POLY1305: + return true; + default: + return false; + } } // Because encryption keys are sensitive and userdebug builds are used by large user pools diff --git a/core/java/android/net/IpSecTransform.java b/core/java/android/net/IpSecTransform.java index 44a0a4f3e18e..fa1497dcbc43 100644 --- a/core/java/android/net/IpSecTransform.java +++ b/core/java/android/net/IpSecTransform.java @@ -21,6 +21,7 @@ import static com.android.internal.util.Preconditions.checkNotNull; import android.annotation.IntDef; import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.RequiresFeature; import android.annotation.RequiresPermission; import android.annotation.SystemApi; @@ -153,7 +154,7 @@ public final class IpSecTransform implements AutoCloseable { /** * Standard equals. */ - public boolean equals(Object other) { + public boolean equals(@Nullable Object other) { if (this == other) return true; if (!(other instanceof IpSecTransform)) return false; final IpSecTransform rhs = (IpSecTransform) other; diff --git a/core/java/android/net/LinkAddress.java b/core/java/android/net/LinkAddress.java index a9d7f17017c4..d1bdaa078cdc 100644 --- a/core/java/android/net/LinkAddress.java +++ b/core/java/android/net/LinkAddress.java @@ -30,7 +30,6 @@ import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.compat.annotation.UnsupportedAppUsage; import android.os.Build; import android.os.Parcel; @@ -158,7 +157,6 @@ public class LinkAddress implements Parcelable { * @return true if the address is IPv6. * @hide */ - @TestApi @SystemApi public boolean isIpv6() { return address instanceof Inet6Address; @@ -180,7 +178,6 @@ public class LinkAddress implements Parcelable { * @return true if the address is IPv4 or is a mapped IPv4 address. * @hide */ - @TestApi @SystemApi public boolean isIpv4() { return address instanceof Inet4Address; @@ -243,7 +240,6 @@ public class LinkAddress implements Parcelable { * @hide */ @SystemApi - @TestApi public LinkAddress(@NonNull InetAddress address, @IntRange(from = 0, to = 128) int prefixLength, int flags, int scope) { init(address, prefixLength, flags, scope, LIFETIME_UNKNOWN, LIFETIME_UNKNOWN); @@ -275,7 +271,6 @@ public class LinkAddress implements Parcelable { * @hide */ @SystemApi - @TestApi public LinkAddress(@NonNull InetAddress address, @IntRange(from = 0, to = 128) int prefixLength, int flags, int scope, long deprecationTime, long expirationTime) { init(address, prefixLength, flags, scope, deprecationTime, expirationTime); @@ -289,7 +284,6 @@ public class LinkAddress implements Parcelable { * @hide */ @SystemApi - @TestApi public LinkAddress(@NonNull InetAddress address, @IntRange(from = 0, to = 128) int prefixLength) { this(address, prefixLength, 0, 0); @@ -314,7 +308,6 @@ public class LinkAddress implements Parcelable { * @hide */ @SystemApi - @TestApi public LinkAddress(@NonNull String address) { this(address, 0, 0); this.scope = scopeForUnicastAddress(this.address); @@ -329,7 +322,6 @@ public class LinkAddress implements Parcelable { * @hide */ @SystemApi - @TestApi public LinkAddress(@NonNull String address, int flags, int scope) { // This may throw an IllegalArgumentException; catching it is the caller's responsibility. // TODO: consider rejecting mapped IPv4 addresses such as "::ffff:192.0.2.5/24". @@ -357,7 +349,7 @@ public class LinkAddress implements Parcelable { * @return {@code true} if both objects are equal, {@code false} otherwise. */ @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { if (!(obj instanceof LinkAddress)) { return false; } @@ -389,7 +381,6 @@ public class LinkAddress implements Parcelable { * otherwise. * @hide */ - @TestApi @SystemApi public boolean isSameAddressAs(@Nullable LinkAddress other) { if (other == null) { @@ -469,7 +460,6 @@ public class LinkAddress implements Parcelable { * @hide */ @SystemApi - @TestApi public long getDeprecationTime() { return deprecationTime; } @@ -485,7 +475,6 @@ public class LinkAddress implements Parcelable { * @hide */ @SystemApi - @TestApi public long getExpirationTime() { return expirationTime; } @@ -496,7 +485,6 @@ public class LinkAddress implements Parcelable { * * @hide */ - @TestApi @SystemApi public boolean isGlobalPreferred() { /** diff --git a/core/java/android/net/LinkProperties.java b/core/java/android/net/LinkProperties.java index 651494d1c99c..25a76f43553a 100644 --- a/core/java/android/net/LinkProperties.java +++ b/core/java/android/net/LinkProperties.java @@ -19,10 +19,8 @@ package android.net; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.compat.annotation.UnsupportedAppUsage; import android.net.util.LinkPropertiesUtils; -import android.net.util.LinkPropertiesUtils.CompareResult; import android.os.Build; import android.os.Parcel; import android.os.Parcelable; @@ -162,7 +160,6 @@ public final class LinkProperties implements Parcelable { * @hide */ @SystemApi - @TestApi public LinkProperties(@Nullable LinkProperties source) { this(source, false /* parcelSensitiveFields */); } @@ -178,7 +175,6 @@ public final class LinkProperties implements Parcelable { * @hide */ @SystemApi - @TestApi public LinkProperties(@Nullable LinkProperties source, boolean parcelSensitiveFields) { mParcelSensitiveFields = parcelSensitiveFields; if (source == null) return; @@ -293,7 +289,6 @@ public final class LinkProperties implements Parcelable { * @hide */ @SystemApi - @TestApi public boolean addLinkAddress(@NonNull LinkAddress address) { if (address == null) { return false; @@ -322,7 +317,6 @@ public final class LinkProperties implements Parcelable { * @hide */ @SystemApi - @TestApi public boolean removeLinkAddress(@NonNull LinkAddress toRemove) { int i = findLinkAddressIndex(toRemove); if (i >= 0) { @@ -376,7 +370,6 @@ public final class LinkProperties implements Parcelable { * @return true if the DNS server was added, false if it was already present. * @hide */ - @TestApi @SystemApi public boolean addDnsServer(@NonNull InetAddress dnsServer) { if (dnsServer != null && !mDnses.contains(dnsServer)) { @@ -393,7 +386,6 @@ public final class LinkProperties implements Parcelable { * @return true if the DNS server was removed, false if it did not exist. * @hide */ - @TestApi @SystemApi public boolean removeDnsServer(@NonNull InetAddress dnsServer) { return mDnses.remove(dnsServer); @@ -428,7 +420,6 @@ public final class LinkProperties implements Parcelable { * @param usePrivateDns The private DNS state. * @hide */ - @TestApi @SystemApi public void setUsePrivateDns(boolean usePrivateDns) { mUsePrivateDns = usePrivateDns; @@ -455,7 +446,6 @@ public final class LinkProperties implements Parcelable { * @param privateDnsServerName The private DNS server name. * @hide */ - @TestApi @SystemApi public void setPrivateDnsServerName(@Nullable String privateDnsServerName) { mPrivateDnsServerName = privateDnsServerName; @@ -534,7 +524,6 @@ public final class LinkProperties implements Parcelable { * object. * @hide */ - @TestApi @SystemApi public void setValidatedPrivateDnsServers(@NonNull Collection<InetAddress> dnsServers) { mValidatedPrivateDnses.clear(); @@ -551,7 +540,6 @@ public final class LinkProperties implements Parcelable { * DNS servers on this link. * @hide */ - @TestApi @SystemApi public @NonNull List<InetAddress> getValidatedPrivateDnsServers() { return Collections.unmodifiableList(mValidatedPrivateDnses); @@ -592,7 +580,6 @@ public final class LinkProperties implements Parcelable { * @hide */ @SystemApi - @TestApi public void setPcscfServers(@NonNull Collection<InetAddress> pcscfServers) { mPcscfs.clear(); for (InetAddress pcscfServer: pcscfServers) { @@ -608,7 +595,6 @@ public final class LinkProperties implements Parcelable { * @hide */ @SystemApi - @TestApi public @NonNull List<InetAddress> getPcscfServers() { return Collections.unmodifiableList(mPcscfs); } @@ -662,7 +648,6 @@ public final class LinkProperties implements Parcelable { * * @hide */ - @TestApi @SystemApi public void setTcpBufferSizes(@Nullable String tcpBufferSizes) { mTcpBufferSizes = tcpBufferSizes; @@ -675,7 +660,6 @@ public final class LinkProperties implements Parcelable { * * @hide */ - @TestApi @SystemApi public @Nullable String getTcpBufferSizes() { return mTcpBufferSizes; @@ -744,7 +728,6 @@ public final class LinkProperties implements Parcelable { * * @hide */ - @TestApi @SystemApi public boolean removeRoute(@NonNull RouteInfo route) { return Objects.equals(mIfaceName, route.getInterface()) && mRoutes.remove(route); @@ -1021,7 +1004,6 @@ public final class LinkProperties implements Parcelable { * @return {@code true} if there is an IPv4 address, {@code false} otherwise. * @hide */ - @TestApi @SystemApi public boolean hasIpv4Address() { for (LinkAddress address : mLinkAddresses) { @@ -1062,7 +1044,6 @@ public final class LinkProperties implements Parcelable { * @return {@code true} if there is a global preferred IPv6 address, {@code false} otherwise. * @hide */ - @TestApi @SystemApi public boolean hasGlobalIpv6Address() { for (LinkAddress address : mLinkAddresses) { @@ -1149,7 +1130,6 @@ public final class LinkProperties implements Parcelable { * @return {@code true} if there is an IPv6 default route, {@code false} otherwise. * @hide */ - @TestApi @SystemApi public boolean hasIpv6DefaultRoute() { for (RouteInfo r : mRoutes) { @@ -1265,7 +1245,6 @@ public final class LinkProperties implements Parcelable { * @return {@code true} if the link is provisioned, {@code false} otherwise. * @hide */ - @TestApi @SystemApi public boolean isIpv4Provisioned() { return (hasIpv4Address() @@ -1280,7 +1259,6 @@ public final class LinkProperties implements Parcelable { * @return {@code true} if the link is provisioned, {@code false} otherwise. * @hide */ - @TestApi @SystemApi public boolean isIpv6Provisioned() { return (hasGlobalIpv6Address() @@ -1308,7 +1286,6 @@ public final class LinkProperties implements Parcelable { * @return {@code true} if the link is provisioned, {@code false} otherwise. * @hide */ - @TestApi @SystemApi public boolean isProvisioned() { return (isIpv4Provisioned() || isIpv6Provisioned()); @@ -1321,7 +1298,6 @@ public final class LinkProperties implements Parcelable { * {@code false} otherwise. * @hide */ - @TestApi @SystemApi public boolean isReachable(@NonNull InetAddress ip) { final List<RouteInfo> allRoutes = getAllRoutes(); @@ -1578,7 +1554,6 @@ public final class LinkProperties implements Parcelable { * @hide */ @SystemApi - @TestApi public void setCaptivePortalApiUrl(@Nullable Uri url) { mCaptivePortalApiUrl = url; } @@ -1593,7 +1568,6 @@ public final class LinkProperties implements Parcelable { * @hide */ @SystemApi - @TestApi @Nullable public Uri getCaptivePortalApiUrl() { return mCaptivePortalApiUrl; @@ -1604,7 +1578,6 @@ public final class LinkProperties implements Parcelable { * @hide */ @SystemApi - @TestApi public void setCaptivePortalData(@Nullable CaptivePortalData data) { mCaptivePortalData = data; } @@ -1618,7 +1591,6 @@ public final class LinkProperties implements Parcelable { * @hide */ @SystemApi - @TestApi @Nullable public CaptivePortalData getCaptivePortalData() { return mCaptivePortalData; @@ -1639,7 +1611,7 @@ public final class LinkProperties implements Parcelable { * @return {@code true} if both objects are equal, {@code false} otherwise. */ @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { if (this == obj) return true; if (!(obj instanceof LinkProperties)) return false; @@ -1669,78 +1641,6 @@ public final class LinkProperties implements Parcelable { } /** - * Compares the DNS addresses in this LinkProperties with another - * LinkProperties, examining only DNS addresses on the base link. - * - * @param target a LinkProperties with the new list of dns addresses - * @return the differences between the DNS addresses. - * @hide - */ - public @NonNull CompareResult<InetAddress> compareDnses(@Nullable LinkProperties target) { - /* - * Duplicate the InetAddresses into removed, we will be removing - * dns address which are common between mDnses and target - * leaving the addresses that are different. And dns address which - * are in target but not in mDnses are placed in the - * addedAddresses. - */ - return new CompareResult<>(mDnses, target != null ? target.getDnsServers() : null); - } - - /** - * Compares the validated private DNS addresses in this LinkProperties with another - * LinkProperties. - * - * @param target a LinkProperties with the new list of validated private dns addresses - * @return the differences between the DNS addresses. - * @hide - */ - public @NonNull CompareResult<InetAddress> compareValidatedPrivateDnses( - @Nullable LinkProperties target) { - return new CompareResult<>(mValidatedPrivateDnses, - target != null ? target.getValidatedPrivateDnsServers() : null); - } - - /** - * Compares all routes in this LinkProperties with another LinkProperties, - * examining both the the base link and all stacked links. - * - * @param target a LinkProperties with the new list of routes - * @return the differences between the routes. - * @hide - */ - public @NonNull CompareResult<RouteInfo> compareAllRoutes(@Nullable LinkProperties target) { - /* - * Duplicate the RouteInfos into removed, we will be removing - * routes which are common between mRoutes and target - * leaving the routes that are different. And route address which - * are in target but not in mRoutes are placed in added. - */ - return new CompareResult<>(getAllRoutes(), target != null ? target.getAllRoutes() : null); - } - - /** - * Compares all interface names in this LinkProperties with another - * LinkProperties, examining both the the base link and all stacked links. - * - * @param target a LinkProperties with the new list of interface names - * @return the differences between the interface names. - * @hide - */ - public @NonNull CompareResult<String> compareAllInterfaceNames( - @Nullable LinkProperties target) { - /* - * Duplicate the interface names into removed, we will be removing - * interface names which are common between this and target - * leaving the interface names that are different. And interface names which - * are in target but not in this are placed in added. - */ - return new CompareResult<>(getAllInterfaceNames(), - target != null ? target.getAllInterfaceNames() : null); - } - - - /** * Generate hashcode based on significant fields * * Equal objects must produce the same hash code, while unequal objects diff --git a/core/java/android/net/MacAddress.java b/core/java/android/net/MacAddress.java index 0eb3c1e8ad01..6949bf2a7807 100644 --- a/core/java/android/net/MacAddress.java +++ b/core/java/android/net/MacAddress.java @@ -160,7 +160,7 @@ public final class MacAddress implements Parcelable { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { return (o instanceof MacAddress) && ((MacAddress) o).mAddr == mAddr; } diff --git a/core/java/android/net/MatchAllNetworkSpecifier.java b/core/java/android/net/MatchAllNetworkSpecifier.java index 70c4a7235b9d..84985b6584c8 100644 --- a/core/java/android/net/MatchAllNetworkSpecifier.java +++ b/core/java/android/net/MatchAllNetworkSpecifier.java @@ -17,6 +17,7 @@ package android.net; import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.SystemApi; import android.os.Parcel; import android.os.Parcelable; @@ -55,7 +56,7 @@ public final class MatchAllNetworkSpecifier extends NetworkSpecifier implements } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { return o instanceof MatchAllNetworkSpecifier; } diff --git a/core/java/android/net/Network.java b/core/java/android/net/Network.java index b872617ab31e..3e4f73555e58 100644 --- a/core/java/android/net/Network.java +++ b/core/java/android/net/Network.java @@ -17,8 +17,8 @@ package android.net; import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.compat.annotation.UnsupportedAppUsage; import android.os.Parcel; import android.os.Parcelable; @@ -127,7 +127,6 @@ public class Network implements Parcelable { * @hide */ @SystemApi - @TestApi public Network(@NonNull Network that) { this(that.netId, that.mPrivateDnsBypass); } @@ -164,7 +163,6 @@ public class Network implements Parcelable { * * @hide */ - @TestApi @SystemApi public @NonNull Network getPrivateDnsBypassingCopy() { return new Network(netId, true); @@ -175,7 +173,6 @@ public class Network implements Parcelable { * * @hide */ - @TestApi @SystemApi public int getNetId() { return netId; @@ -500,7 +497,7 @@ public class Network implements Parcelable { }; @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { if (!(obj instanceof Network)) return false; Network other = (Network)obj; return this.netId == other.netId; diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java index 004f84422b44..be33f4edb5d1 100644 --- a/core/java/android/net/NetworkCapabilities.java +++ b/core/java/android/net/NetworkCapabilities.java @@ -850,7 +850,6 @@ public final class NetworkCapabilities implements Parcelable { * @return an array of transport type values for this instance. * @hide */ - @TestApi @SystemApi @NonNull public @Transport int[] getTransportTypes() { return BitUtils.unpackBits(mTransportTypes); @@ -1025,7 +1024,6 @@ public final class NetworkCapabilities implements Parcelable { */ @NonNull @SystemApi - @TestApi public int[] getAdministratorUids() { return Arrays.copyOf(mAdministratorUids, mAdministratorUids.length); } @@ -1506,7 +1504,6 @@ public final class NetworkCapabilities implements Parcelable { * @hide */ @SystemApi - @TestApi public @Nullable String getSsid() { return mSSID; } @@ -1590,7 +1587,6 @@ public final class NetworkCapabilities implements Parcelable { * * @hide */ - @TestApi @SystemApi public boolean satisfiedByNetworkCapabilities(@Nullable NetworkCapabilities nc) { return satisfiedByNetworkCapabilities(nc, false); @@ -2136,7 +2132,6 @@ public final class NetworkCapabilities implements Parcelable { * @hide */ @SystemApi - @TestApi public static final class Builder { private final NetworkCapabilities mCaps; diff --git a/core/java/android/net/NetworkIdentity.java b/core/java/android/net/NetworkIdentity.java index 0948a4da1a3a..9c1038846d70 100644 --- a/core/java/android/net/NetworkIdentity.java +++ b/core/java/android/net/NetworkIdentity.java @@ -20,6 +20,7 @@ import static android.net.ConnectivityManager.TYPE_WIFI; import static android.net.ConnectivityManager.getNetworkTypeName; import static android.net.ConnectivityManager.isNetworkTypeMobile; +import android.annotation.Nullable; import android.content.Context; import android.net.wifi.WifiInfo; import android.net.wifi.WifiManager; @@ -69,7 +70,7 @@ public class NetworkIdentity implements Comparable<NetworkIdentity> { } @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { if (obj instanceof NetworkIdentity) { final NetworkIdentity ident = (NetworkIdentity) obj; return mType == ident.mType && mSubType == ident.mSubType && mRoaming == ident.mRoaming diff --git a/core/java/android/net/NetworkPolicy.java b/core/java/android/net/NetworkPolicy.java index 4f05c9bbb2e3..8a0211c2ec2f 100644 --- a/core/java/android/net/NetworkPolicy.java +++ b/core/java/android/net/NetworkPolicy.java @@ -16,6 +16,7 @@ package android.net; +import android.annotation.Nullable; import android.compat.annotation.UnsupportedAppUsage; import android.os.Parcel; import android.os.Parcelable; @@ -205,7 +206,7 @@ public class NetworkPolicy implements Parcelable, Comparable<NetworkPolicy> { } @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { if (obj instanceof NetworkPolicy) { final NetworkPolicy other = (NetworkPolicy) obj; return warningBytes == other.warningBytes diff --git a/core/java/android/net/NetworkProvider.java b/core/java/android/net/NetworkProvider.java index 75086cf82b57..d31218d9b67b 100644 --- a/core/java/android/net/NetworkProvider.java +++ b/core/java/android/net/NetworkProvider.java @@ -30,7 +30,7 @@ import android.util.Log; /** * Base class for network providers such as telephony or Wi-Fi. NetworkProviders connect the device - * to networks and makes them available to to the core network stack by creating + * to networks and makes them available to the core network stack by creating * {@link NetworkAgent}s. The networks can then provide connectivity to apps and can be interacted * with via networking APIs such as {@link ConnectivityManager}. * diff --git a/core/java/android/net/NetworkRequest.java b/core/java/android/net/NetworkRequest.java index 473e6c5a7287..1d6e50710df1 100644 --- a/core/java/android/net/NetworkRequest.java +++ b/core/java/android/net/NetworkRequest.java @@ -553,7 +553,7 @@ public class NetworkRequest implements Parcelable { proto.end(token); } - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { if (obj instanceof NetworkRequest == false) return false; NetworkRequest that = (NetworkRequest)obj; return (that.legacyType == this.legacyType && diff --git a/core/java/android/net/NetworkScorerAppData.java b/core/java/android/net/NetworkScorerAppData.java index 116e39ec53aa..caa6e455abc0 100644 --- a/core/java/android/net/NetworkScorerAppData.java +++ b/core/java/android/net/NetworkScorerAppData.java @@ -110,7 +110,7 @@ public final class NetworkScorerAppData implements Parcelable { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; NetworkScorerAppData that = (NetworkScorerAppData) o; diff --git a/core/java/android/net/NetworkStack.java b/core/java/android/net/NetworkStack.java index 86f3dfd412f6..79f9e6ef2a97 100644 --- a/core/java/android/net/NetworkStack.java +++ b/core/java/android/net/NetworkStack.java @@ -33,7 +33,6 @@ import java.util.Arrays; * @hide */ @SystemApi -@TestApi public class NetworkStack { /** * Permission granted only to the NetworkStack APK, defined in NetworkStackStub with signature @@ -41,7 +40,6 @@ public class NetworkStack { * @hide */ @SystemApi - @TestApi public static final String PERMISSION_MAINLINE_NETWORK_STACK = "android.permission.MAINLINE_NETWORK_STACK"; @@ -54,7 +52,6 @@ public class NetworkStack { */ @Nullable @SystemApi - @TestApi public static IBinder getService() { final IBinder mockService = sMockService; if (mockService != null) return mockService; diff --git a/core/java/android/net/NetworkStats.java b/core/java/android/net/NetworkStats.java index 34e48eb44f32..cbee01024622 100644 --- a/core/java/android/net/NetworkStats.java +++ b/core/java/android/net/NetworkStats.java @@ -411,7 +411,7 @@ public final class NetworkStats implements Parcelable { /** @hide */ @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (o instanceof Entry) { final Entry e = (Entry) o; return uid == e.uid && set == e.set && tag == e.tag && metered == e.metered diff --git a/core/java/android/net/NetworkTemplate.java b/core/java/android/net/NetworkTemplate.java index cd26079a7bac..a95ba12f6544 100644 --- a/core/java/android/net/NetworkTemplate.java +++ b/core/java/android/net/NetworkTemplate.java @@ -327,7 +327,7 @@ public class NetworkTemplate implements Parcelable { } @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { if (obj instanceof NetworkTemplate) { final NetworkTemplate other = (NetworkTemplate) obj; return mMatchRule == other.mMatchRule diff --git a/core/java/android/net/ProxyInfo.java b/core/java/android/net/ProxyInfo.java index a32b41f6be4b..de5a1800a97e 100644 --- a/core/java/android/net/ProxyInfo.java +++ b/core/java/android/net/ProxyInfo.java @@ -275,7 +275,7 @@ public class ProxyInfo implements Parcelable { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (!(o instanceof ProxyInfo)) return false; ProxyInfo p = (ProxyInfo)o; // If PAC URL is present in either then they must be equal. diff --git a/core/java/android/net/RouteInfo.java b/core/java/android/net/RouteInfo.java index 98760761736d..93ad41f7c524 100644 --- a/core/java/android/net/RouteInfo.java +++ b/core/java/android/net/RouteInfo.java @@ -20,7 +20,6 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.compat.annotation.UnsupportedAppUsage; import android.net.util.NetUtils; import android.os.Build; @@ -87,17 +86,14 @@ public final class RouteInfo implements Parcelable { /** Unicast route. @hide */ @SystemApi - @TestApi public static final int RTN_UNICAST = 1; /** Unreachable route. @hide */ @SystemApi - @TestApi public static final int RTN_UNREACHABLE = 7; /** Throw route. @hide */ @SystemApi - @TestApi public static final int RTN_THROW = 9; /** @@ -135,7 +131,6 @@ public final class RouteInfo implements Parcelable { * @hide */ @SystemApi - @TestApi public RouteInfo(@Nullable IpPrefix destination, @Nullable InetAddress gateway, @Nullable String iface, @RouteType int type) { this(destination, gateway, iface, type, 0); @@ -397,7 +392,6 @@ public final class RouteInfo implements Parcelable { * * @hide */ - @TestApi @SystemApi @RouteType public int getType() { @@ -539,7 +533,7 @@ public final class RouteInfo implements Parcelable { * Compares this RouteInfo object against the specified object and indicates if they are equal. * @return {@code true} if the objects are equal, {@code false} otherwise. */ - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { if (this == obj) return true; if (!(obj instanceof RouteInfo)) return false; @@ -575,7 +569,7 @@ public final class RouteInfo implements Parcelable { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (!(o instanceof RouteKey)) { return false; } diff --git a/core/java/android/net/StaticIpConfiguration.java b/core/java/android/net/StaticIpConfiguration.java index a973455baa04..f56d656f07ed 100644 --- a/core/java/android/net/StaticIpConfiguration.java +++ b/core/java/android/net/StaticIpConfiguration.java @@ -19,7 +19,6 @@ package android.net; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.compat.annotation.UnsupportedAppUsage; import android.os.Parcel; import android.os.Parcelable; @@ -52,7 +51,6 @@ import java.util.Objects; * @hide */ @SystemApi -@TestApi public final class StaticIpConfiguration implements Parcelable { /** @hide */ @UnsupportedAppUsage diff --git a/core/java/android/net/StringNetworkSpecifier.java b/core/java/android/net/StringNetworkSpecifier.java index 3f2aa17f263e..012410b6b7b7 100644 --- a/core/java/android/net/StringNetworkSpecifier.java +++ b/core/java/android/net/StringNetworkSpecifier.java @@ -17,6 +17,7 @@ package android.net; import android.annotation.NonNull; +import android.annotation.Nullable; import android.os.Parcel; import android.os.Parcelable; import android.text.TextUtils; @@ -45,7 +46,7 @@ public final class StringNetworkSpecifier extends NetworkSpecifier implements Pa } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (!(o instanceof StringNetworkSpecifier)) return false; return TextUtils.equals(specifier, ((StringNetworkSpecifier) o).specifier); } diff --git a/core/java/android/net/TEST_MAPPING b/core/java/android/net/TEST_MAPPING new file mode 100644 index 000000000000..abac81117deb --- /dev/null +++ b/core/java/android/net/TEST_MAPPING @@ -0,0 +1,20 @@ +{ + "imports": [ + { + // Also includes cts/tests/tests/net + "path": "frameworks/base/tests/net" + }, + { + "path": "packages/modules/NetworkStack" + }, + { + "path": "packages/modules/CaptivePortalLogin" + }, + { + "path": "frameworks/base/packages/Tethering" + }, + { + "path": "frameworks/opt/net/wifi" + } + ] +}
\ No newline at end of file diff --git a/core/java/android/net/TelephonyNetworkSpecifier.java b/core/java/android/net/TelephonyNetworkSpecifier.java index 33c71d5b312a..334823373e0d 100644 --- a/core/java/android/net/TelephonyNetworkSpecifier.java +++ b/core/java/android/net/TelephonyNetworkSpecifier.java @@ -17,6 +17,7 @@ package android.net; import android.annotation.NonNull; +import android.annotation.Nullable; import android.os.Parcel; import android.os.Parcelable; @@ -74,7 +75,7 @@ public final class TelephonyNetworkSpecifier extends NetworkSpecifier implements } @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { if (this == obj) { return true; } diff --git a/core/java/android/net/UidRange.java b/core/java/android/net/UidRange.java index d75c43ddb318..3bc0f9ca4e6a 100644 --- a/core/java/android/net/UidRange.java +++ b/core/java/android/net/UidRange.java @@ -18,6 +18,7 @@ package android.net; import static android.os.UserHandle.PER_USER_RANGE; +import android.annotation.Nullable; import android.os.Parcel; import android.os.Parcelable; @@ -81,7 +82,7 @@ public final class UidRange implements Parcelable { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (this == o) { return true; } diff --git a/core/java/android/net/Uri.java b/core/java/android/net/Uri.java index efe2903814d7..815e4f0c9071 100644 --- a/core/java/android/net/Uri.java +++ b/core/java/android/net/Uri.java @@ -354,7 +354,7 @@ public abstract class Uri implements Parcelable, Comparable<Uri> { * default port explicitly and the other leaves it implicit, they will not * be considered equal. */ - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (!(o instanceof Uri)) { return false; } diff --git a/core/java/android/net/apf/ApfCapabilities.java b/core/java/android/net/apf/ApfCapabilities.java index 92c543294ae1..bf5b26e278f9 100644 --- a/core/java/android/net/apf/ApfCapabilities.java +++ b/core/java/android/net/apf/ApfCapabilities.java @@ -19,7 +19,6 @@ package android.net.apf; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.content.res.Resources; import android.os.Parcel; import android.os.Parcelable; @@ -36,7 +35,6 @@ import com.android.internal.R; * @hide */ @SystemApi -@TestApi public final class ApfCapabilities implements Parcelable { /** * Version of APF instruction set supported for packet filtering. 0 indicates no support for diff --git a/core/java/android/net/metrics/ApfProgramEvent.java b/core/java/android/net/metrics/ApfProgramEvent.java index f93907a37f3f..c50bae90488b 100644 --- a/core/java/android/net/metrics/ApfProgramEvent.java +++ b/core/java/android/net/metrics/ApfProgramEvent.java @@ -20,7 +20,6 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.compat.annotation.UnsupportedAppUsage; import android.os.Parcel; import android.os.Parcelable; @@ -40,7 +39,6 @@ import java.util.List; * the APF program in place with a new APF program. * {@hide} */ -@TestApi @SystemApi public final class ApfProgramEvent implements IpConnectivityLog.Event { diff --git a/core/java/android/net/metrics/ApfStats.java b/core/java/android/net/metrics/ApfStats.java index b221cb97b28f..2a601b273ef4 100644 --- a/core/java/android/net/metrics/ApfStats.java +++ b/core/java/android/net/metrics/ApfStats.java @@ -19,7 +19,6 @@ package android.net.metrics; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.compat.annotation.UnsupportedAppUsage; import android.os.Parcel; import android.os.Parcelable; @@ -29,7 +28,6 @@ import android.os.Parcelable; * {@hide} */ @SystemApi -@TestApi public final class ApfStats implements IpConnectivityLog.Event { /** @@ -126,7 +124,6 @@ public final class ApfStats implements IpConnectivityLog.Event { * @hide */ @SystemApi - @TestApi public static final class Builder { private long mDurationMs; private int mReceivedRas; diff --git a/core/java/android/net/metrics/DhcpClientEvent.java b/core/java/android/net/metrics/DhcpClientEvent.java index 8fc1ef80bba9..e0a93dd1c18f 100644 --- a/core/java/android/net/metrics/DhcpClientEvent.java +++ b/core/java/android/net/metrics/DhcpClientEvent.java @@ -19,7 +19,6 @@ package android.net.metrics; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.compat.annotation.UnsupportedAppUsage; import android.os.Parcel; import android.os.Parcelable; @@ -30,7 +29,6 @@ import android.text.TextUtils; * {@hide} */ @SystemApi -@TestApi public final class DhcpClientEvent implements IpConnectivityLog.Event { // Names for recording DhcpClient pseudo-state transitions. diff --git a/core/java/android/net/metrics/DhcpErrorEvent.java b/core/java/android/net/metrics/DhcpErrorEvent.java index 32efb5adafcd..de3129d5e94d 100644 --- a/core/java/android/net/metrics/DhcpErrorEvent.java +++ b/core/java/android/net/metrics/DhcpErrorEvent.java @@ -18,7 +18,6 @@ package android.net.metrics; import android.annotation.NonNull; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.os.Parcel; import android.os.Parcelable; import android.util.SparseArray; @@ -30,7 +29,6 @@ import com.android.internal.util.MessageUtils; * {@hide} */ @SystemApi -@TestApi public final class DhcpErrorEvent implements IpConnectivityLog.Event { public static final int L2_ERROR = 1; public static final int L3_ERROR = 2; diff --git a/core/java/android/net/metrics/IpConnectivityLog.java b/core/java/android/net/metrics/IpConnectivityLog.java index 680c01573f98..a008d855025a 100644 --- a/core/java/android/net/metrics/IpConnectivityLog.java +++ b/core/java/android/net/metrics/IpConnectivityLog.java @@ -18,7 +18,6 @@ package android.net.metrics; import android.annotation.NonNull; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.net.ConnectivityMetricsEvent; import android.net.IIpConnectivityMetrics; import android.net.Network; @@ -35,7 +34,6 @@ import com.android.internal.util.BitUtils; * {@hide} */ @SystemApi -@TestApi public class IpConnectivityLog { private static final String TAG = IpConnectivityLog.class.getSimpleName(); private static final boolean DBG = false; @@ -52,7 +50,6 @@ public class IpConnectivityLog { /** @hide */ @SystemApi - @TestApi public IpConnectivityLog() { } diff --git a/core/java/android/net/metrics/IpManagerEvent.java b/core/java/android/net/metrics/IpManagerEvent.java index f14abb895cb4..4f7f3263117b 100644 --- a/core/java/android/net/metrics/IpManagerEvent.java +++ b/core/java/android/net/metrics/IpManagerEvent.java @@ -20,7 +20,6 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.os.Parcel; import android.os.Parcelable; import android.util.SparseArray; @@ -36,7 +35,6 @@ import java.lang.annotation.RetentionPolicy; * {@hide} */ @SystemApi -@TestApi public final class IpManagerEvent implements IpConnectivityLog.Event { public static final int PROVISIONING_OK = 1; diff --git a/core/java/android/net/metrics/IpReachabilityEvent.java b/core/java/android/net/metrics/IpReachabilityEvent.java index 79e01d7116b6..d5003badd614 100644 --- a/core/java/android/net/metrics/IpReachabilityEvent.java +++ b/core/java/android/net/metrics/IpReachabilityEvent.java @@ -19,7 +19,6 @@ package android.net.metrics; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.os.Parcel; import android.os.Parcelable; import android.util.SparseArray; @@ -32,7 +31,6 @@ import com.android.internal.util.MessageUtils; * {@hide} */ @SystemApi -@TestApi public final class IpReachabilityEvent implements IpConnectivityLog.Event { // Event types. diff --git a/core/java/android/net/metrics/NetworkEvent.java b/core/java/android/net/metrics/NetworkEvent.java index fe603cf9305c..8c28f7a7d643 100644 --- a/core/java/android/net/metrics/NetworkEvent.java +++ b/core/java/android/net/metrics/NetworkEvent.java @@ -20,7 +20,6 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.os.Parcel; import android.os.Parcelable; import android.util.SparseArray; @@ -34,7 +33,6 @@ import java.lang.annotation.RetentionPolicy; * {@hide} */ @SystemApi -@TestApi public final class NetworkEvent implements IpConnectivityLog.Event { public static final int NETWORK_CONNECTED = 1; diff --git a/core/java/android/net/metrics/RaEvent.java b/core/java/android/net/metrics/RaEvent.java index 661f648fc74e..b54874f5a573 100644 --- a/core/java/android/net/metrics/RaEvent.java +++ b/core/java/android/net/metrics/RaEvent.java @@ -19,7 +19,6 @@ package android.net.metrics; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.os.Parcel; import android.os.Parcelable; @@ -28,7 +27,6 @@ import android.os.Parcelable; * {@hide} */ @SystemApi -@TestApi public final class RaEvent implements IpConnectivityLog.Event { private static final long NO_LIFETIME = -1L; diff --git a/core/java/android/net/metrics/ValidationProbeEvent.java b/core/java/android/net/metrics/ValidationProbeEvent.java index 8fab64ae6c4e..7f4e4a73677e 100644 --- a/core/java/android/net/metrics/ValidationProbeEvent.java +++ b/core/java/android/net/metrics/ValidationProbeEvent.java @@ -20,7 +20,6 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.os.Parcel; import android.os.Parcelable; import android.util.SparseArray; @@ -35,7 +34,6 @@ import java.lang.annotation.RetentionPolicy; * {@hide} */ @SystemApi -@TestApi public final class ValidationProbeEvent implements IpConnectivityLog.Event { public static final int PROBE_DNS = 0; diff --git a/core/java/android/net/util/SocketUtils.java b/core/java/android/net/util/SocketUtils.java index 696708408c22..e64060f1b220 100644 --- a/core/java/android/net/util/SocketUtils.java +++ b/core/java/android/net/util/SocketUtils.java @@ -22,7 +22,6 @@ import static android.system.OsConstants.SO_BINDTODEVICE; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.net.NetworkUtils; import android.system.ErrnoException; import android.system.NetlinkSocketAddress; @@ -40,7 +39,6 @@ import java.net.SocketAddress; * @hide */ @SystemApi -@TestApi public final class SocketUtils { /** * Create a raw datagram socket that is bound to an interface. diff --git a/core/java/android/nfc/NdefMessage.java b/core/java/android/nfc/NdefMessage.java index 35dae55f1c80..553f6c01b016 100644 --- a/core/java/android/nfc/NdefMessage.java +++ b/core/java/android/nfc/NdefMessage.java @@ -16,6 +16,7 @@ package android.nfc; +import android.annotation.Nullable; import android.os.Parcel; import android.os.Parcelable; import android.util.proto.ProtoOutputStream; @@ -23,7 +24,6 @@ import android.util.proto.ProtoOutputStream; import java.nio.ByteBuffer; import java.util.Arrays; - /** * Represents an immutable NDEF Message. * <p> @@ -239,7 +239,7 @@ public final class NdefMessage implements Parcelable { * identical NDEF Records. */ @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; diff --git a/core/java/android/nfc/NdefRecord.java b/core/java/android/nfc/NdefRecord.java index 74f8cb4c1d3d..421eb33392db 100644 --- a/core/java/android/nfc/NdefRecord.java +++ b/core/java/android/nfc/NdefRecord.java @@ -16,6 +16,7 @@ package android.nfc; +import android.annotation.Nullable; import android.compat.annotation.UnsupportedAppUsage; import android.content.Intent; import android.net.Uri; @@ -1032,7 +1033,7 @@ public final class NdefRecord implements Parcelable { * identical tnf, type, id and payload fields. */ @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; diff --git a/core/java/android/nfc/cardemulation/ApduServiceInfo.java b/core/java/android/nfc/cardemulation/ApduServiceInfo.java index d7c2e0522b0f..e3f996bdb6dc 100644 --- a/core/java/android/nfc/cardemulation/ApduServiceInfo.java +++ b/core/java/android/nfc/cardemulation/ApduServiceInfo.java @@ -16,6 +16,7 @@ package android.nfc.cardemulation; +import android.annotation.Nullable; import android.compat.annotation.UnsupportedAppUsage; import android.content.ComponentName; import android.content.pm.PackageManager; @@ -507,7 +508,7 @@ public final class ApduServiceInfo implements Parcelable { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (this == o) return true; if (!(o instanceof ApduServiceInfo)) return false; ApduServiceInfo thatService = (ApduServiceInfo) o; diff --git a/core/java/android/nfc/cardemulation/NfcFServiceInfo.java b/core/java/android/nfc/cardemulation/NfcFServiceInfo.java index 885a0b540646..c2b33dd51b0c 100644 --- a/core/java/android/nfc/cardemulation/NfcFServiceInfo.java +++ b/core/java/android/nfc/cardemulation/NfcFServiceInfo.java @@ -16,6 +16,7 @@ package android.nfc.cardemulation; +import android.annotation.Nullable; import android.content.ComponentName; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; @@ -250,7 +251,7 @@ public final class NfcFServiceInfo implements Parcelable { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (this == o) return true; if (!(o instanceof NfcFServiceInfo)) return false; NfcFServiceInfo thatService = (NfcFServiceInfo) o; diff --git a/core/java/android/os/BatteryManager.java b/core/java/android/os/BatteryManager.java index 12ec0a0c21d5..9a16d3fee600 100644 --- a/core/java/android/os/BatteryManager.java +++ b/core/java/android/os/BatteryManager.java @@ -20,7 +20,6 @@ import android.Manifest.permission; import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.annotation.SystemService; -import android.annotation.TestApi; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.content.Intent; @@ -392,7 +391,6 @@ public class BatteryManager { */ @RequiresPermission(permission.POWER_SAVER) @SystemApi - @TestApi public boolean setChargingStateUpdateDelayMillis(int delayMillis) { try { return mBatteryStats.setChargingStateUpdateDelayMillis(delayMillis); diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java index d889b155cf64..88525895d9ec 100644 --- a/core/java/android/os/BatteryStats.java +++ b/core/java/android/os/BatteryStats.java @@ -1499,7 +1499,7 @@ public abstract class BatteryStats implements Parcelable { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java index d492e0870b2e..fce3437db049 100644 --- a/core/java/android/os/Binder.java +++ b/core/java/android/os/Binder.java @@ -379,6 +379,7 @@ public class Binder implements IBinder { * * @see #clearCallingIdentity */ + @CriticalNative public static final native void restoreCallingIdentity(long token); /** diff --git a/core/java/android/os/BugreportManager.java b/core/java/android/os/BugreportManager.java index 9e996d15fa84..fe4d7296503f 100644 --- a/core/java/android/os/BugreportManager.java +++ b/core/java/android/os/BugreportManager.java @@ -24,13 +24,13 @@ import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.annotation.SystemService; -import android.annotation.TestApi; import android.app.ActivityManager; import android.content.Context; import android.content.Intent; import android.os.Handler; import android.util.Log; import android.widget.Toast; + import com.android.internal.R; import com.android.internal.util.Preconditions; @@ -48,7 +48,6 @@ import java.util.concurrent.Executor; * @hide */ @SystemApi -@TestApi @SystemService(Context.BUGREPORT_SERVICE) public final class BugreportManager { diff --git a/core/java/android/os/BugreportParams.java b/core/java/android/os/BugreportParams.java index c834781346f6..279ccae7c94f 100644 --- a/core/java/android/os/BugreportParams.java +++ b/core/java/android/os/BugreportParams.java @@ -18,7 +18,6 @@ package android.os; import android.annotation.IntDef; import android.annotation.SystemApi; -import android.annotation.TestApi; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -29,7 +28,6 @@ import java.lang.annotation.RetentionPolicy; * @hide */ @SystemApi -@TestApi public final class BugreportParams { private final int mMode; diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java index 78ba7f0eec28..bd18150da201 100755 --- a/core/java/android/os/Build.java +++ b/core/java/android/os/Build.java @@ -18,6 +18,7 @@ package android.os; import android.Manifest; import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SuppressAutoDoc; import android.annotation.SystemApi; @@ -1215,7 +1216,7 @@ public class Build { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (!(o instanceof Partition)) { return false; } diff --git a/core/java/android/os/CoolingDevice.java b/core/java/android/os/CoolingDevice.java index 0e86a381932d..4babd4b524c9 100644 --- a/core/java/android/os/CoolingDevice.java +++ b/core/java/android/os/CoolingDevice.java @@ -18,6 +18,7 @@ package android.os; import android.annotation.IntDef; import android.annotation.NonNull; +import android.annotation.Nullable; import android.hardware.thermal.V2_0.CoolingType; import com.android.internal.util.Preconditions; @@ -128,7 +129,7 @@ public final class CoolingDevice implements Parcelable { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (!(o instanceof CoolingDevice)) { return false; } diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java index 085681d412e9..5745187fcbb9 100644 --- a/core/java/android/os/Environment.java +++ b/core/java/android/os/Environment.java @@ -256,7 +256,6 @@ public class Environment { * @hide */ @SystemApi - @TestApi public static @NonNull File getOemDirectory() { return DIR_OEM_ROOT; } @@ -268,7 +267,6 @@ public class Environment { * @hide */ @SystemApi - @TestApi public static @NonNull File getOdmDirectory() { return DIR_ODM_ROOT; } @@ -279,7 +277,6 @@ public class Environment { * @hide */ @SystemApi - @TestApi public static @NonNull File getVendorDirectory() { return DIR_VENDOR_ROOT; } @@ -291,7 +288,6 @@ public class Environment { * @hide */ @SystemApi - @TestApi public static @NonNull File getProductDirectory() { return DIR_PRODUCT_ROOT; } @@ -318,7 +314,6 @@ public class Environment { * @hide */ @SystemApi - @TestApi public static @NonNull File getSystemExtDirectory() { return DIR_SYSTEM_EXT_ROOT; } diff --git a/core/java/android/os/ExternalVibration.java b/core/java/android/os/ExternalVibration.java index e62244f5dea0..7fd02116814b 100644 --- a/core/java/android/os/ExternalVibration.java +++ b/core/java/android/os/ExternalVibration.java @@ -17,6 +17,7 @@ package android.os; import android.annotation.NonNull; +import android.annotation.Nullable; import android.media.AudioAttributes; import android.util.Slog; @@ -137,7 +138,7 @@ public class ExternalVibration implements Parcelable { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (o == null || !(o instanceof ExternalVibration)) { return false; } diff --git a/core/java/android/os/FileUtils.java b/core/java/android/os/FileUtils.java index 70c924a46c17..bbafc7b0875a 100644 --- a/core/java/android/os/FileUtils.java +++ b/core/java/android/os/FileUtils.java @@ -40,9 +40,16 @@ import static android.system.OsConstants.W_OK; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.TestApi; +import android.app.AppGlobals; import android.compat.annotation.UnsupportedAppUsage; import android.content.ContentResolver; +import android.content.Context; +import android.content.pm.PackageManager; +import android.content.pm.ProviderInfo; +import android.content.res.AssetFileDescriptor; +import android.net.Uri; import android.provider.DocumentsContract.Document; +import android.provider.MediaStore; import android.system.ErrnoException; import android.system.Os; import android.system.StructStat; @@ -118,6 +125,7 @@ public final class FileUtils { // non-final so it can be toggled by Robolectric's ShadowFileUtils private static boolean sEnableCopyOptimizations = true; + private static volatile int sMediaProviderAppId = -1; private static final long COPY_CHECKPOINT_BYTES = 524288; @@ -1425,6 +1433,54 @@ public final class FileUtils { } /** {@hide} */ + public static FileDescriptor convertToModernFd(FileDescriptor fd) { + try { + Context context = AppGlobals.getInitialApplication(); + if (UserHandle.getAppId(Process.myUid()) == getMediaProviderAppId(context)) { + // Never convert modern fd for MediaProvider, because this requires + // MediaStore#scanFile and can cause infinite loops when MediaProvider scans + return null; + } + File realFile = ParcelFileDescriptor.getFile(fd); + Log.i(TAG, "Changing to modern format dataSource for: " + realFile); + ContentResolver resolver = context.getContentResolver(); + + Uri uri = MediaStore.scanFile(resolver, realFile); + if (uri != null) { + Bundle opts = new Bundle(); + // TODO(b/158465539): Use API constant + opts.putBoolean("android.provider.extra.ACCEPT_ORIGINAL_MEDIA_FORMAT", true); + AssetFileDescriptor afd = resolver.openTypedAssetFileDescriptor(uri, "*/*", opts); + Log.i(TAG, "Changed to modern format dataSource for: " + realFile); + return afd.getFileDescriptor(); + } else { + Log.i(TAG, "Failed to change to modern format dataSource for: " + realFile); + } + } catch (Exception e) { + Log.w(TAG, "Failed to change to modern format dataSource"); + } + return null; + } + + private static int getMediaProviderAppId(Context context) { + if (sMediaProviderAppId != -1) { + return sMediaProviderAppId; + } + + PackageManager pm = context.getPackageManager(); + ProviderInfo provider = context.getPackageManager().resolveContentProvider( + MediaStore.AUTHORITY, PackageManager.MATCH_DIRECT_BOOT_AWARE + | PackageManager.MATCH_DIRECT_BOOT_UNAWARE + | PackageManager.MATCH_SYSTEM_ONLY); + if (provider == null) { + return -1; + } + + sMediaProviderAppId = UserHandle.getAppId(provider.applicationInfo.uid); + return sMediaProviderAppId; + } + + /** {@hide} */ @VisibleForTesting public static class MemoryPipe extends Thread implements AutoCloseable { private final FileDescriptor[] pipe; diff --git a/core/java/android/os/GraphicsEnvironment.java b/core/java/android/os/GraphicsEnvironment.java index 6ba1627dde47..be21fea1d0df 100644 --- a/core/java/android/os/GraphicsEnvironment.java +++ b/core/java/android/os/GraphicsEnvironment.java @@ -109,11 +109,11 @@ public class GraphicsEnvironment { private static final int UPDATABLE_DRIVER_GLOBAL_OPT_IN_PRERELEASE_DRIVER = 2; private static final int UPDATABLE_DRIVER_GLOBAL_OPT_IN_OFF = 3; - // Values for GLOBAL_SETTINGS_ANGLE_GL_DRIVER_ALL_ANGLE + // Values for ANGLE_GL_DRIVER_ALL_ANGLE private static final int ANGLE_GL_DRIVER_ALL_ANGLE_ON = 1; private static final int ANGLE_GL_DRIVER_ALL_ANGLE_OFF = 0; - // Values for GLOBAL_SETTINGS_ANGLE_GL_DRIVER_SELECTION_VALUES + // Values for ANGLE_GL_DRIVER_SELECTION_VALUES private static final String ANGLE_GL_DRIVER_CHOICE_DEFAULT = "default"; private static final String ANGLE_GL_DRIVER_CHOICE_ANGLE = "angle"; private static final String ANGLE_GL_DRIVER_CHOICE_NATIVE = "native"; @@ -382,11 +382,11 @@ public class GraphicsEnvironment { final int allUseAngle; if (bundle != null) { allUseAngle = - bundle.getInt(Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_ALL_ANGLE); + bundle.getInt(Settings.Global.ANGLE_GL_DRIVER_ALL_ANGLE); } else { ContentResolver contentResolver = context.getContentResolver(); allUseAngle = Settings.Global.getInt(contentResolver, - Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_ALL_ANGLE, + Settings.Global.ANGLE_GL_DRIVER_ALL_ANGLE, ANGLE_GL_DRIVER_ALL_ANGLE_OFF); } if (allUseAngle == ANGLE_GL_DRIVER_ALL_ANGLE_ON) { @@ -402,10 +402,10 @@ public class GraphicsEnvironment { final ContentResolver contentResolver = context.getContentResolver(); final List<String> optInPackages = getGlobalSettingsString(contentResolver, bundle, - Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_SELECTION_PKGS); + Settings.Global.ANGLE_GL_DRIVER_SELECTION_PKGS); final List<String> optInValues = getGlobalSettingsString(contentResolver, bundle, - Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_SELECTION_VALUES); + Settings.Global.ANGLE_GL_DRIVER_SELECTION_VALUES); // Make sure we have good settings to use if (optInPackages.size() != optInValues.size()) { @@ -462,11 +462,11 @@ public class GraphicsEnvironment { if (coreSettings != null) { debugPackage = - coreSettings.getString(Settings.Global.GLOBAL_SETTINGS_ANGLE_DEBUG_PACKAGE); + coreSettings.getString(Settings.Global.ANGLE_DEBUG_PACKAGE); } else { ContentResolver contentResolver = context.getContentResolver(); debugPackage = Settings.Global.getString(contentResolver, - Settings.Global.GLOBAL_SETTINGS_ANGLE_DEBUG_PACKAGE); + Settings.Global.ANGLE_DEBUG_PACKAGE); } if (TextUtils.isEmpty(debugPackage)) { return ""; @@ -578,7 +578,7 @@ public class GraphicsEnvironment { final ContentResolver contentResolver = context.getContentResolver(); final List<String> angleAllowlist = getGlobalSettingsString(contentResolver, bundle, - Settings.Global.GLOBAL_SETTINGS_ANGLE_ALLOWLIST); + Settings.Global.ANGLE_ALLOWLIST); if (DEBUG) Log.v(TAG, "ANGLE allowlist: " + angleAllowlist); @@ -678,7 +678,7 @@ public class GraphicsEnvironment { try { ContentResolver contentResolver = context.getContentResolver(); final int showDialogBox = Settings.Global.getInt(contentResolver, - Settings.Global.GLOBAL_SETTINGS_SHOW_ANGLE_IN_USE_DIALOG_BOX); + Settings.Global.SHOW_ANGLE_IN_USE_DIALOG_BOX); return (showDialogBox == 1); } catch (Settings.SettingNotFoundException | SecurityException e) { diff --git a/core/java/android/os/HidlMemory.java b/core/java/android/os/HidlMemory.java index 02d1e0ce9109..26fc6f0ae4ce 100644 --- a/core/java/android/os/HidlMemory.java +++ b/core/java/android/os/HidlMemory.java @@ -20,7 +20,6 @@ import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; -import android.annotation.TestApi; import java.io.Closeable; import java.io.IOException; @@ -41,7 +40,6 @@ import java.io.IOException; * @hide */ @SystemApi -@TestApi public class HidlMemory implements Closeable { private final @NonNull String mName; private final long mSize; diff --git a/core/java/android/os/HwBinder.java b/core/java/android/os/HwBinder.java index 64ab1d711765..0d2bfdf04905 100644 --- a/core/java/android/os/HwBinder.java +++ b/core/java/android/os/HwBinder.java @@ -17,7 +17,6 @@ package android.os; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.compat.annotation.UnsupportedAppUsage; import libcore.util.NativeAllocationRegistry; @@ -26,7 +25,6 @@ import java.util.NoSuchElementException; /** @hide */ @SystemApi -@TestApi public abstract class HwBinder implements IHwBinder { private static final String TAG = "HwBinder"; diff --git a/core/java/android/os/HwBlob.java b/core/java/android/os/HwBlob.java index 154227b2a786..a43fbdbde4d2 100644 --- a/core/java/android/os/HwBlob.java +++ b/core/java/android/os/HwBlob.java @@ -19,7 +19,6 @@ package android.os; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; -import android.annotation.TestApi; import libcore.util.NativeAllocationRegistry; @@ -30,7 +29,6 @@ import libcore.util.NativeAllocationRegistry; * @hide */ @SystemApi -@TestApi public class HwBlob { private static final String TAG = "HwBlob"; diff --git a/core/java/android/os/HwParcel.java b/core/java/android/os/HwParcel.java index 228548ad7802..9fd37d4548ac 100644 --- a/core/java/android/os/HwParcel.java +++ b/core/java/android/os/HwParcel.java @@ -20,7 +20,6 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.compat.annotation.UnsupportedAppUsage; import dalvik.annotation.optimization.FastNative; @@ -34,7 +33,6 @@ import java.util.Arrays; /** @hide */ @SystemApi -@TestApi public class HwParcel { private static final String TAG = "HwParcel"; diff --git a/core/java/android/os/IHwBinder.java b/core/java/android/os/IHwBinder.java index 46fa6ef3b783..249eb3aa3456 100644 --- a/core/java/android/os/IHwBinder.java +++ b/core/java/android/os/IHwBinder.java @@ -17,11 +17,9 @@ package android.os; import android.annotation.SystemApi; -import android.annotation.TestApi; /** @hide */ @SystemApi -@TestApi public interface IHwBinder { /** * Process a hwbinder transaction. diff --git a/core/java/android/os/IHwInterface.java b/core/java/android/os/IHwInterface.java index 0a5a71550b06..f21f6e30a041 100644 --- a/core/java/android/os/IHwInterface.java +++ b/core/java/android/os/IHwInterface.java @@ -17,11 +17,9 @@ package android.os; import android.annotation.SystemApi; -import android.annotation.TestApi; /** @hide */ @SystemApi -@TestApi public interface IHwInterface { /** * @return the binder object that corresponds to this interface. diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl index 07363edd3e75..6fe57774f6f3 100644 --- a/core/java/android/os/IUserManager.aidl +++ b/core/java/android/os/IUserManager.aidl @@ -86,6 +86,7 @@ interface IUserManager { Bundle getApplicationRestrictionsForUser(in String packageName, int userId); void setDefaultGuestRestrictions(in Bundle restrictions); Bundle getDefaultGuestRestrictions(); + int removeUserOrSetEphemeral(int userId); boolean markGuestForDeletion(int userId); UserInfo findCurrentGuestUser(); boolean isQuietModeEnabled(int userId); diff --git a/core/java/android/os/IncidentManager.java b/core/java/android/os/IncidentManager.java index 565d31a26dbc..a543a2d6a983 100644 --- a/core/java/android/os/IncidentManager.java +++ b/core/java/android/os/IncidentManager.java @@ -23,7 +23,6 @@ import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.annotation.SystemService; -import android.annotation.TestApi; import android.content.Context; import android.net.Uri; import android.util.Slog; @@ -45,7 +44,6 @@ import java.util.concurrent.Executor; * @hide */ @SystemApi -@TestApi @SystemService(Context.INCIDENT_SERVICE) public class IncidentManager { private static final String TAG = "IncidentManager"; @@ -159,7 +157,6 @@ public class IncidentManager { * @hide */ @SystemApi - @TestApi public static class PendingReport { /** * Encoded data. @@ -277,7 +274,6 @@ public class IncidentManager { * @hide */ @SystemApi - @TestApi public static class IncidentReport implements Parcelable, Closeable { private final long mTimestampNs; private final int mPrivacyPolicy; diff --git a/core/java/android/os/IncidentReportArgs.java b/core/java/android/os/IncidentReportArgs.java index 7e858e1dc390..73e4914ce6ae 100644 --- a/core/java/android/os/IncidentReportArgs.java +++ b/core/java/android/os/IncidentReportArgs.java @@ -18,7 +18,6 @@ package android.os; import android.annotation.NonNull; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.os.Parcel; import android.os.Parcelable; import android.util.IntArray; @@ -30,7 +29,6 @@ import java.util.ArrayList; * {@hide} */ @SystemApi -@TestApi public final class IncidentReportArgs implements Parcelable { private final IntArray mSections = new IntArray(); diff --git a/core/java/android/os/LocaleList.java b/core/java/android/os/LocaleList.java index 9c0bc45a346e..ee64551df226 100644 --- a/core/java/android/os/LocaleList.java +++ b/core/java/android/os/LocaleList.java @@ -93,7 +93,7 @@ public final class LocaleList implements Parcelable { } @Override - public boolean equals(Object other) { + public boolean equals(@Nullable Object other) { if (other == this) return true; if (!(other instanceof LocaleList)) diff --git a/core/java/android/os/Messenger.java b/core/java/android/os/Messenger.java index ed5c4700fc97..ed851b307ff8 100644 --- a/core/java/android/os/Messenger.java +++ b/core/java/android/os/Messenger.java @@ -16,6 +16,8 @@ package android.os; +import android.annotation.Nullable; + /** * Reference to a Handler, which others can use to send messages to it. * This allows for the implementation of message-based communication across @@ -71,7 +73,7 @@ public final class Messenger implements Parcelable { * Comparison operator on two Messenger objects, such that true * is returned then they both point to the same Handler. */ - public boolean equals(Object otherObj) { + public boolean equals(@Nullable Object otherObj) { if (otherObj == null) { return false; } diff --git a/core/java/android/os/NativeHandle.java b/core/java/android/os/NativeHandle.java index 8d341b603eb4..a26873a87552 100644 --- a/core/java/android/os/NativeHandle.java +++ b/core/java/android/os/NativeHandle.java @@ -20,7 +20,6 @@ import static android.system.OsConstants.F_DUPFD_CLOEXEC; import android.annotation.NonNull; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.system.ErrnoException; import android.system.Os; @@ -33,7 +32,6 @@ import java.io.FileDescriptor; * @hide */ @SystemApi -@TestApi public final class NativeHandle implements Closeable { // whether this object owns mFds private boolean mOwn = false; diff --git a/core/java/android/os/ParcelUuid.java b/core/java/android/os/ParcelUuid.java index 0b52c7501d5b..b529694b6033 100644 --- a/core/java/android/os/ParcelUuid.java +++ b/core/java/android/os/ParcelUuid.java @@ -16,6 +16,7 @@ package android.os; +import android.annotation.Nullable; import android.compat.annotation.UnsupportedAppUsage; import java.util.UUID; @@ -91,7 +92,7 @@ public final class ParcelUuid implements Parcelable { * or {@code false} if not. */ @Override - public boolean equals(Object object) { + public boolean equals(@Nullable Object object) { if (object == null) { return false; } diff --git a/core/java/android/os/Parcelable.java b/core/java/android/os/Parcelable.java index f14f66b07630..7a624e1da26c 100644 --- a/core/java/android/os/Parcelable.java +++ b/core/java/android/os/Parcelable.java @@ -120,7 +120,7 @@ public interface Parcelable { * @see ParcelableHolder * @hide */ - @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) + @SystemApi(client = SystemApi.Client.PRIVILEGED_APPS) public static final int PARCELABLE_STABILITY_LOCAL = 0x0000; /** * Something that is meant to be used between system and vendor. @@ -128,7 +128,7 @@ public interface Parcelable { * @see ParcelableHolder * @hide */ - @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) + @SystemApi(client = SystemApi.Client.PRIVILEGED_APPS) public static final int PARCELABLE_STABILITY_VINTF = 0x0001; /** diff --git a/core/java/android/os/ParcelableHolder.java b/core/java/android/os/ParcelableHolder.java index 181f94b39841..95c07b6b2451 100644 --- a/core/java/android/os/ParcelableHolder.java +++ b/core/java/android/os/ParcelableHolder.java @@ -18,12 +18,54 @@ package android.os; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.SystemApi; import android.util.MathUtils; /** - * Parcelable containing the other Parcelable object. + * ParcelableHolder is a Parcelable which can contain another Parcelable. + * The main use case of ParcelableHolder is to make a Parcelable extensible. + * For example, an AOSP-defined Parcelable <code>AospDefinedParcelable</code> + * is expected to be extended by device implementers for their value-add features. + * Previously without ParcelableHolder, the device implementers had to + * directly modify the Parcelable to add more fields: + * <pre> {@code + * parcelable AospDefinedParcelable { + * int a; + * String b; + * String x; // added by a device implementer + * int[] y; // added by a device implementer + * }}</pre> + * + * This practice is very error-prone because the fields added by the device implementer + * might have a conflict when the Parcelable is revisioned in the next releases of Android. + * + * Using ParcelableHolder, one can define an extension point in a Parcelable. + * <pre> {@code + * parcelable AospDefinedParcelable { + * int a; + * String b; + * ParcelableHolder extension; + * }}</pre> + * Then the device implementers can define their own Parcelable for their extension. + * + * <pre> {@code + * parcelable OemDefinedParcelable { + * String x; + * int[] y; + * }}</pre> + * Finally, the new Parcelable can be attached to the original Parcelable via + * the ParcelableHolder field. + * + * <pre> {@code + * AospDefinedParcelable ap = ...; + * OemDefinedParcelable op = new OemDefinedParcelable(); + * op.x = ...; + * op.y = ...; + * ap.extension.setParcelable(op);}</pre> + * * @hide */ +@SystemApi public final class ParcelableHolder implements Parcelable { /** * This is set by {@link #setParcelable}. @@ -80,7 +122,7 @@ public final class ParcelableHolder implements Parcelable { * Write a parcelable into ParcelableHolder, the previous parcelable will be removed. * @return {@code false} if the parcelable's stability is more unstable ParcelableHolder. */ - public synchronized boolean setParcelable(@Nullable Parcelable p) { + public boolean setParcelable(@Nullable Parcelable p) { // a ParcelableHolder can only hold things at its stability or higher if (p != null && this.getStability() > p.getStability()) { return false; @@ -99,7 +141,7 @@ public final class ParcelableHolder implements Parcelable { * the type written by (@link #setParcelable}. */ @Nullable - public synchronized <T extends Parcelable> T getParcelable(@NonNull Class<T> clazz) { + public <T extends Parcelable> T getParcelable(@NonNull Class<T> clazz) { if (mParcel == null) { if (!clazz.isInstance(mParcelable)) { return null; @@ -123,7 +165,7 @@ public final class ParcelableHolder implements Parcelable { /** * Read ParcelableHolder from a parcel. */ - public synchronized void readFromParcel(@NonNull Parcel parcel) { + public void readFromParcel(@NonNull Parcel parcel) { this.mStability = parcel.readInt(); mParcelable = null; @@ -145,7 +187,7 @@ public final class ParcelableHolder implements Parcelable { } @Override - public synchronized void writeToParcel(@NonNull Parcel parcel, int flags) { + public void writeToParcel(@NonNull Parcel parcel, int flags) { parcel.writeInt(this.mStability); if (mParcel != null) { @@ -166,7 +208,7 @@ public final class ParcelableHolder implements Parcelable { } @Override - public synchronized int describeContents() { + public int describeContents() { if (mParcel != null) { return mParcel.hasFileDescriptors() ? Parcelable.CONTENTS_FILE_DESCRIPTOR : 0; } diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java index 50f0c28cb8f8..000b23f5d5ca 100644 --- a/core/java/android/os/PowerManager.java +++ b/core/java/android/os/PowerManager.java @@ -1565,7 +1565,6 @@ public final class PowerManager { * @see #isPowerSaveMode() */ @SystemApi - @TestApi @RequiresPermission(anyOf = { android.Manifest.permission.DEVICE_POWER, android.Manifest.permission.POWER_SAVER @@ -1610,7 +1609,6 @@ public final class PowerManager { * @hide */ @SystemApi - @TestApi @RequiresPermission(permission.POWER_SAVER) public boolean setDynamicPowerSaveHint(boolean powerSaveHint, int disableThreshold) { try { @@ -1670,7 +1668,6 @@ public final class PowerManager { * @hide */ @SystemApi - @TestApi public static final int POWER_SAVE_MODE_TRIGGER_PERCENTAGE = 0; /** @@ -1683,7 +1680,6 @@ public final class PowerManager { * @hide */ @SystemApi - @TestApi public static final int POWER_SAVE_MODE_TRIGGER_DYNAMIC = 1; /** @hide */ @@ -1708,7 +1704,6 @@ public final class PowerManager { */ @AutoPowerSaveModeTriggers @SystemApi - @TestApi @RequiresPermission(android.Manifest.permission.POWER_SAVER) public int getPowerSaveModeTrigger() { try { @@ -1732,7 +1727,6 @@ public final class PowerManager { * @hide */ @SystemApi - @TestApi @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public void setBatteryDischargePrediction(@NonNull Duration timeRemaining, boolean isPersonalized) { diff --git a/core/java/android/os/RemoteCallback.java b/core/java/android/os/RemoteCallback.java index 373060f4ff4b..49f84adf2c21 100644 --- a/core/java/android/os/RemoteCallback.java +++ b/core/java/android/os/RemoteCallback.java @@ -19,14 +19,12 @@ package android.os; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.compat.annotation.UnsupportedAppUsage; /** * @hide */ @SystemApi -@TestApi public final class RemoteCallback implements Parcelable { public interface OnResultListener { diff --git a/core/java/android/os/SystemConfigManager.java b/core/java/android/os/SystemConfigManager.java index 12a1ffaf69c1..3f0632be90d1 100644 --- a/core/java/android/os/SystemConfigManager.java +++ b/core/java/android/os/SystemConfigManager.java @@ -20,7 +20,6 @@ import android.annotation.NonNull; import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.annotation.SystemService; -import android.annotation.TestApi; import android.content.Context; import android.util.ArraySet; import android.util.Log; @@ -40,7 +39,6 @@ import java.util.Set; * @hide */ @SystemApi -@TestApi @SystemService(Context.SYSTEM_CONFIG_SERVICE) public class SystemConfigManager { private static final String TAG = SystemConfigManager.class.getSimpleName(); diff --git a/core/java/android/os/SystemProperties.java b/core/java/android/os/SystemProperties.java index c5e5cc40d54e..a16452705efc 100644 --- a/core/java/android/os/SystemProperties.java +++ b/core/java/android/os/SystemProperties.java @@ -19,7 +19,6 @@ package android.os; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.compat.annotation.UnsupportedAppUsage; import android.util.Log; import android.util.MutableInt; @@ -52,7 +51,6 @@ import java.util.HashMap; * {@hide} */ @SystemApi -@TestApi public class SystemProperties { private static final String TAG = "SystemProperties"; private static final boolean TRACK_KEY_ACCESS = false; @@ -146,7 +144,6 @@ public class SystemProperties { */ @NonNull @SystemApi - @TestApi public static String get(@NonNull String key) { if (TRACK_KEY_ACCESS) onKeyAccess(key); return native_get(key); @@ -163,7 +160,6 @@ public class SystemProperties { */ @NonNull @SystemApi - @TestApi public static String get(@NonNull String key, @Nullable String def) { if (TRACK_KEY_ACCESS) onKeyAccess(key); return native_get(key, def); @@ -179,7 +175,6 @@ public class SystemProperties { * @hide */ @SystemApi - @TestApi public static int getInt(@NonNull String key, int def) { if (TRACK_KEY_ACCESS) onKeyAccess(key); return native_get_int(key, def); @@ -195,7 +190,6 @@ public class SystemProperties { * @hide */ @SystemApi - @TestApi public static long getLong(@NonNull String key, long def) { if (TRACK_KEY_ACCESS) onKeyAccess(key); return native_get_long(key, def); @@ -216,7 +210,6 @@ public class SystemProperties { * @hide */ @SystemApi - @TestApi public static boolean getBoolean(@NonNull String key, boolean def) { if (TRACK_KEY_ACCESS) onKeyAccess(key); return native_get_boolean(key, def); diff --git a/core/java/android/os/Temperature.java b/core/java/android/os/Temperature.java index 7caffcd2ddfd..55785f3bdba9 100644 --- a/core/java/android/os/Temperature.java +++ b/core/java/android/os/Temperature.java @@ -18,6 +18,7 @@ package android.os; import android.annotation.IntDef; import android.annotation.NonNull; +import android.annotation.Nullable; import android.hardware.thermal.V2_0.TemperatureType; import android.hardware.thermal.V2_0.ThrottlingSeverity; @@ -171,7 +172,7 @@ public final class Temperature implements Parcelable { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (!(o instanceof Temperature)) { return false; } diff --git a/core/java/android/os/TimestampedValue.java b/core/java/android/os/TimestampedValue.java index 4c4335bc7867..3d8a5504b56d 100644 --- a/core/java/android/os/TimestampedValue.java +++ b/core/java/android/os/TimestampedValue.java @@ -60,7 +60,7 @@ public final class TimestampedValue<T> implements Parcelable { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (this == o) { return true; } diff --git a/core/java/android/os/UserHandle.java b/core/java/android/os/UserHandle.java index ff1bcf6b3c5e..d39c5328e330 100644 --- a/core/java/android/os/UserHandle.java +++ b/core/java/android/os/UserHandle.java @@ -18,6 +18,7 @@ package android.os; import android.annotation.AppIdInt; import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.SystemApi; import android.annotation.TestApi; import android.annotation.UserIdInt; @@ -46,7 +47,6 @@ public final class UserHandle implements Parcelable { /** @hide A user handle to indicate all users on the device */ @SystemApi - @TestApi public static final @NonNull UserHandle ALL = new UserHandle(USER_ALL); /** @hide A user id to indicate the currently active user */ @@ -55,7 +55,6 @@ public final class UserHandle implements Parcelable { /** @hide A user handle to indicate the current user of the device */ @SystemApi - @TestApi public static final @NonNull UserHandle CURRENT = new UserHandle(USER_CURRENT); /** @hide A user id to indicate that we would like to send to the current @@ -106,7 +105,6 @@ public final class UserHandle implements Parcelable { /** @hide A user handle to indicate the "system" user of the device */ @SystemApi - @TestApi public static final @NonNull UserHandle SYSTEM = new UserHandle(USER_SYSTEM); /** @@ -279,7 +277,6 @@ public final class UserHandle implements Parcelable { } /** @hide */ - @TestApi @SystemApi public static UserHandle of(@UserIdInt int userId) { if (userId == USER_SYSTEM) { @@ -324,7 +321,6 @@ public final class UserHandle implements Parcelable { * Returns the app id (or base uid) for a given uid, stripping out the user id from it. * @hide */ - @TestApi @SystemApi public static @AppIdInt int getAppId(int uid) { return uid % PER_USER_RANGE; @@ -482,7 +478,6 @@ public final class UserHandle implements Parcelable { * @hide */ @SystemApi - @TestApi public static @UserIdInt int myUserId() { return getUserId(Process.myUid()); } @@ -521,7 +516,6 @@ public final class UserHandle implements Parcelable { * @hide */ @SystemApi - @TestApi public @UserIdInt int getIdentifier() { return mHandle; } @@ -532,7 +526,7 @@ public final class UserHandle implements Parcelable { } @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { try { if (obj != null) { UserHandle other = (UserHandle)obj; diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java index ddc21ab2c8c0..b0e76e3ea851 100644 --- a/core/java/android/os/UserManager.java +++ b/core/java/android/os/UserManager.java @@ -1407,8 +1407,7 @@ public class UserManager { * * @hide */ - @SystemApi - @TestApi // To allow seeing it from CTS. + @SystemApi // To allow seeing it from CTS. public static final String ACTION_USER_RESTRICTIONS_CHANGED = "android.os.action.USER_RESTRICTIONS_CHANGED"; @@ -1468,6 +1467,48 @@ public class UserManager { public @interface UserSwitchabilityResult {} /** + * A response code from {@link #removeUserOrSetEphemeral(int)} indicating that the specified + * user has been successfully removed. + * @hide + */ + public static final int REMOVE_RESULT_REMOVED = 0; + + /** + * A response code from {@link #removeUserOrSetEphemeral(int)} indicating that the specified + * user has had its {@link UserInfo#FLAG_EPHEMERAL} flag set to {@code true}, so that it will be + * removed when the user is stopped or on boot. + * @hide + */ + public static final int REMOVE_RESULT_SET_EPHEMERAL = 1; + + /** + * A response code from {@link #removeUserOrSetEphemeral(int)} indicating that the specified + * user is already in the process of being removed. + * @hide + */ + public static final int REMOVE_RESULT_ALREADY_BEING_REMOVED = 2; + + /** + * A response code from {@link #removeUserOrSetEphemeral(int)} indicating that an error occurred + * that prevented the user from being removed or set as ephemeral. + * @hide + */ + public static final int REMOVE_RESULT_ERROR = 3; + + /** + * Possible response codes from {@link #removeUserOrSetEphemeral(int)}. + * @hide + */ + @IntDef(prefix = { "REMOVE_RESULT_" }, value = { + REMOVE_RESULT_REMOVED, + REMOVE_RESULT_SET_EPHEMERAL, + REMOVE_RESULT_ALREADY_BEING_REMOVED, + REMOVE_RESULT_ERROR, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface RemoveResult {} + + /** * Indicates user operation is successful. */ public static final int USER_OPERATION_SUCCESS = 0; @@ -3978,6 +4019,23 @@ public class UserManager { } /** + * Immediately removes the user or, if the user cannot be removed, such as when the user is + * the current user, then set the user as ephemeral so that it will be removed when it is + * stopped. + * + * @return the {@link RemoveResult} code + * @hide + */ + @RequiresPermission(android.Manifest.permission.MANAGE_USERS) + public @RemoveResult int removeUserOrSetEphemeral(@UserIdInt int userId) { + try { + return mService.removeUserOrSetEphemeral(userId); + } catch (RemoteException re) { + throw re.rethrowFromSystemServer(); + } + } + + /** * Updates the user's name. * * @param userId the user's integer id diff --git a/core/java/android/os/VibrationAttributes.java b/core/java/android/os/VibrationAttributes.java index d77861fb71b2..2093077dbfdf 100644 --- a/core/java/android/os/VibrationAttributes.java +++ b/core/java/android/os/VibrationAttributes.java @@ -237,7 +237,7 @@ public final class VibrationAttributes implements Parcelable { }; @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (this == o) { return true; } diff --git a/core/java/android/os/VibrationEffect.java b/core/java/android/os/VibrationEffect.java index e02fd28cc856..487e46886914 100644 --- a/core/java/android/os/VibrationEffect.java +++ b/core/java/android/os/VibrationEffect.java @@ -539,7 +539,7 @@ public abstract class VibrationEffect implements Parcelable { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (!(o instanceof VibrationEffect.OneShot)) { return false; } @@ -705,7 +705,7 @@ public abstract class VibrationEffect implements Parcelable { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (!(o instanceof VibrationEffect.Waveform)) { return false; } @@ -872,7 +872,7 @@ public abstract class VibrationEffect implements Parcelable { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (!(o instanceof VibrationEffect.Prebaked)) { return false; } @@ -998,7 +998,7 @@ public abstract class VibrationEffect implements Parcelable { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Composed composed = (Composed) o; @@ -1244,7 +1244,7 @@ public abstract class VibrationEffect implements Parcelable { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; PrimitiveEffect that = (PrimitiveEffect) o; diff --git a/core/java/android/os/Vibrator.java b/core/java/android/os/Vibrator.java index 0c0e6b5d73a6..7d85d13094a1 100644 --- a/core/java/android/os/Vibrator.java +++ b/core/java/android/os/Vibrator.java @@ -23,7 +23,6 @@ import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.annotation.SystemService; -import android.annotation.TestApi; import android.app.ActivityThread; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; @@ -469,7 +468,6 @@ public abstract class Vibrator { * @hide */ @SystemApi - @TestApi @RequiresPermission(android.Manifest.permission.ACCESS_VIBRATOR_STATE) public boolean isVibrating() { return false; @@ -483,7 +481,6 @@ public abstract class Vibrator { * @hide */ @SystemApi - @TestApi public interface OnVibratorStateChangedListener { /** * Called when the vibrator state has changed. @@ -502,7 +499,6 @@ public abstract class Vibrator { * @hide */ @SystemApi - @TestApi @RequiresPermission(android.Manifest.permission.ACCESS_VIBRATOR_STATE) public void addVibratorStateListener(@NonNull OnVibratorStateChangedListener listener) { } @@ -516,7 +512,6 @@ public abstract class Vibrator { * @hide */ @SystemApi - @TestApi @RequiresPermission(android.Manifest.permission.ACCESS_VIBRATOR_STATE) public void addVibratorStateListener( @NonNull @CallbackExecutor Executor executor, @@ -531,7 +526,6 @@ public abstract class Vibrator { * @hide */ @SystemApi - @TestApi @RequiresPermission(android.Manifest.permission.ACCESS_VIBRATOR_STATE) public void removeVibratorStateListener(@NonNull OnVibratorStateChangedListener listener) { } diff --git a/core/java/android/os/WorkSource.java b/core/java/android/os/WorkSource.java index a1b4dc3ffded..e0927ebd261a 100644 --- a/core/java/android/os/WorkSource.java +++ b/core/java/android/os/WorkSource.java @@ -98,7 +98,6 @@ public class WorkSource implements Parcelable { * @param uid the uid performing the work * @hide */ - @TestApi @SystemApi public WorkSource(int uid) { mNum = 1; @@ -152,7 +151,6 @@ public class WorkSource implements Parcelable { * Returns the number of uids in this work source. * @hide */ - @TestApi @SystemApi public int size() { return mNum; @@ -173,7 +171,6 @@ public class WorkSource implements Parcelable { * If {@code index} < 0 or {@code index} >= {@link #size() N}, then the behavior is undefined. * @hide */ - @TestApi @SystemApi public int getUid(int index) { return mUids[index]; @@ -209,7 +206,6 @@ public class WorkSource implements Parcelable { * If {@code index} < 0 or {@code index} >= {@link #size() N}, then the behavior is undefined. * @hide */ - @TestApi @SystemApi @Nullable public String getPackageName(int index) { @@ -455,7 +451,6 @@ public class WorkSource implements Parcelable { * @hide */ @SystemApi - @TestApi @NonNull public WorkSource withoutNames() { final WorkSource copy = new WorkSource(this); @@ -582,7 +577,6 @@ public class WorkSource implements Parcelable { * @hide for internal use only. */ @SystemApi - @TestApi public boolean isEmpty() { return mNum == 0 && (mChains == null || mChains.isEmpty()); } diff --git a/core/java/android/os/connectivity/CellularBatteryStats.java b/core/java/android/os/connectivity/CellularBatteryStats.java index 121fd333d17f..fc17002ba056 100644 --- a/core/java/android/os/connectivity/CellularBatteryStats.java +++ b/core/java/android/os/connectivity/CellularBatteryStats.java @@ -109,7 +109,7 @@ public final class CellularBatteryStats implements Parcelable { CellSignalStrength.getNumSignalStrengthLevels())); mTxTimeMs = Arrays.copyOfRange( txTimeMs, 0, - Math.min(txTimeMs.length, ModemActivityInfo.TX_POWER_LEVELS)); + Math.min(txTimeMs.length, ModemActivityInfo.getNumTxPowerLevels())); mMonitoredRailChargeConsumedMaMs = monitoredRailChargeConsumedMaMs; } diff --git a/core/java/android/os/image/DynamicSystemClient.java b/core/java/android/os/image/DynamicSystemClient.java index 50d8d8079ed5..58268e2fc914 100644 --- a/core/java/android/os/image/DynamicSystemClient.java +++ b/core/java/android/os/image/DynamicSystemClient.java @@ -22,7 +22,6 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -68,7 +67,6 @@ import java.util.concurrent.Executor; * @hide */ @SystemApi -@TestApi public class DynamicSystemClient { /** @hide */ @IntDef(prefix = { "STATUS_" }, value = { @@ -286,7 +284,6 @@ public class DynamicSystemClient { * @hide */ @SystemApi - @TestApi public DynamicSystemClient(@NonNull Context context) { mContext = context; mConnection = new DynSystemServiceConnection(); @@ -322,7 +319,6 @@ public class DynamicSystemClient { */ @RequiresPermission(android.Manifest.permission.INSTALL_DYNAMIC_SYSTEM) @SystemApi - @TestApi public void bind() { if (!featureFlagEnabled()) { Slog.w(TAG, FeatureFlagUtils.DYNAMIC_SYSTEM + " not enabled; bind() aborted."); @@ -345,7 +341,6 @@ public class DynamicSystemClient { */ @RequiresPermission(android.Manifest.permission.INSTALL_DYNAMIC_SYSTEM) @SystemApi - @TestApi public void unbind() { if (!mBound) { return; @@ -381,7 +376,6 @@ public class DynamicSystemClient { */ @RequiresPermission(android.Manifest.permission.INSTALL_DYNAMIC_SYSTEM) @SystemApi - @TestApi public void start(@NonNull Uri systemUrl, @BytesLong long systemSize) { start(systemUrl, systemSize, 0 /* Use the default userdata size */); } diff --git a/core/java/android/os/incremental/IIncrementalService.aidl b/core/java/android/os/incremental/IIncrementalService.aidl index 52475e9cd89d..ca92ad5deae6 100644 --- a/core/java/android/os/incremental/IIncrementalService.aidl +++ b/core/java/android/os/incremental/IIncrementalService.aidl @@ -144,4 +144,14 @@ interface IIncrementalService { * Stop listening for the loading progress change for a storage. */ boolean unregisterLoadingProgressListener(int storageId); + + /** + * Register storage health status listener. + */ + boolean registerStorageHealthListener(int storageId, in StorageHealthCheckParams params, in IStorageHealthListener listener); + + /** + * Register storage health status listener. + */ + void unregisterStorageHealthListener(int storageId); } diff --git a/core/java/android/os/incremental/IStorageHealthListener.aidl b/core/java/android/os/incremental/IStorageHealthListener.aidl index 9f93ede5c9fc..c71e73f9ec8e 100644 --- a/core/java/android/os/incremental/IStorageHealthListener.aidl +++ b/core/java/android/os/incremental/IStorageHealthListener.aidl @@ -26,9 +26,15 @@ oneway interface IStorageHealthListener { /** There are reads pending for params.blockedTimeoutMs, waiting till * params.unhealthyTimeoutMs to confirm unhealthy state. */ const int HEALTH_STATUS_BLOCKED = 2; - /** There are reads pending for params.unhealthyTimeoutMs>, - * marking storage as unhealthy. */ + /** There are reads pending for params.unhealthyTimeoutMs, + * marking storage as unhealthy due to unknown issues. */ const int HEALTH_STATUS_UNHEALTHY = 3; + /** There are reads pending for params.unhealthyTimeoutMs, + * due to data transportation issues. */ + const int HEALTH_STATUS_UNHEALTHY_TRANSPORT = 4; + /** There are reads pending for params.unhealthyTimeoutMs, + * due to limited storage space. */ + const int HEALTH_STATUS_UNHEALTHY_STORAGE = 5; /** Health status callback. */ void onHealthStatus(in int storageId, in int status); diff --git a/core/java/android/os/incremental/IncrementalManager.java b/core/java/android/os/incremental/IncrementalManager.java index 768ef975bd99..fb47ef04b231 100644 --- a/core/java/android/os/incremental/IncrementalManager.java +++ b/core/java/android/os/incremental/IncrementalManager.java @@ -283,6 +283,7 @@ public final class IncrementalManager { return; } mLoadingProgressCallbacks.cleanUpCallbacks(storage); + unregisterHealthListener(codePath); mService.deleteStorage(storage.getId()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); @@ -297,7 +298,7 @@ public final class IncrementalManager { * @param callback To report loading progress to. * @return True if the package name and associated storage id are valid. False otherwise. */ - public boolean registerCallback(@NonNull String codePath, + public boolean registerLoadingProgressCallback(@NonNull String codePath, @NonNull IPackageLoadingProgressCallback callback) { final IncrementalStorage storage = openStorage(codePath); if (storage == null) { @@ -314,7 +315,7 @@ public final class IncrementalManager { * @param codePath Path of the installed package * @return True if the package name and associated storage id are valid. False otherwise. */ - public boolean unregisterCallback(@NonNull String codePath, + public boolean unregisterLoadingProgressCallback(@NonNull String codePath, @NonNull IPackageLoadingProgressCallback callback) { final IncrementalStorage storage = openStorage(codePath); if (storage == null) { @@ -414,6 +415,38 @@ public final class IncrementalManager { } } + /** + * Specify the health check params and listener for listening to Incremental Storage health + * status changes. Notice that this will overwrite the previously registered listener. + * @param codePath Path of the installed package. This path is on an Incremental Storage. + * @param healthCheckParams The params for health state change timeouts. + * @param listener To report health status change. + * @return True if listener was successfully registered. + */ + public boolean registerHealthListener(@NonNull String codePath, + @NonNull StorageHealthCheckParams healthCheckParams, + @NonNull IStorageHealthListener.Stub listener) { + final IncrementalStorage storage = openStorage(codePath); + if (storage == null) { + // storage does not exist, package not installed + return false; + } + return storage.registerStorageHealthListener(healthCheckParams, listener); + } + + /** + * Stop listening to health status changes on an Incremental Storage. + * @param codePath Path of the installed package. This path is on an Incremental Storage. + */ + public void unregisterHealthListener(@NonNull String codePath) { + final IncrementalStorage storage = openStorage(codePath); + if (storage == null) { + // storage does not exist, package not installed + return; + } + storage.unregisterStorageHealthListener(); + } + /* Native methods */ private static native boolean nativeIsEnabled(); private static native boolean nativeIsIncrementalPath(@NonNull String path); diff --git a/core/java/android/os/incremental/IncrementalStorage.java b/core/java/android/os/incremental/IncrementalStorage.java index a1c3cc697e02..b913faf9cc83 100644 --- a/core/java/android/os/incremental/IncrementalStorage.java +++ b/core/java/android/os/incremental/IncrementalStorage.java @@ -545,4 +545,31 @@ public final class IncrementalStorage { return false; } } + + /** + * Register to listen to the status changes of the storage health. + * @param healthCheckParams Params to specify status change timeouts. + * @param listener To report health status change from Incremental Service to the caller. + */ + public boolean registerStorageHealthListener(StorageHealthCheckParams healthCheckParams, + IStorageHealthListener listener) { + try { + return mService.registerStorageHealthListener(mId, healthCheckParams, listener); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + return false; + } + } + + /** + * Stops listening to the status changes of the storage health. + */ + public void unregisterStorageHealthListener() { + try { + mService.unregisterStorageHealthListener(mId); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + return; + } + } } diff --git a/core/java/android/os/storage/DiskInfo.java b/core/java/android/os/storage/DiskInfo.java index df3c4d55d979..67317c7b2224 100644 --- a/core/java/android/os/storage/DiskInfo.java +++ b/core/java/android/os/storage/DiskInfo.java @@ -184,7 +184,7 @@ public class DiskInfo implements Parcelable { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (o instanceof DiskInfo) { return Objects.equals(id, ((DiskInfo) o).id); } else { diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java index 6b5eb16d7bff..270115beb09b 100644 --- a/core/java/android/os/storage/StorageManager.java +++ b/core/java/android/os/storage/StorageManager.java @@ -43,7 +43,6 @@ import android.annotation.SdkConstant; import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.annotation.SystemService; -import android.annotation.TestApi; import android.annotation.WorkerThread; import android.app.Activity; import android.app.ActivityThread; @@ -1701,7 +1700,6 @@ public class StorageManager { * @hide */ @SystemApi - @TestApi public static boolean hasIsolatedStorage() { // Prefer to use snapshot for current boot when available return SystemProperties.getBoolean(PROP_ISOLATED_STORAGE_SNAPSHOT, diff --git a/core/java/android/os/storage/StorageVolume.java b/core/java/android/os/storage/StorageVolume.java index a63b82e44f06..eed36d714653 100644 --- a/core/java/android/os/storage/StorageVolume.java +++ b/core/java/android/os/storage/StorageVolume.java @@ -435,7 +435,7 @@ public final class StorageVolume implements Parcelable { } @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { if (obj instanceof StorageVolume && mPath != null) { StorageVolume volume = (StorageVolume)obj; return (mPath.equals(volume.mPath)); diff --git a/core/java/android/os/storage/VolumeInfo.java b/core/java/android/os/storage/VolumeInfo.java index d7aaa4d6cbf9..74c0ecb2ecc3 100644 --- a/core/java/android/os/storage/VolumeInfo.java +++ b/core/java/android/os/storage/VolumeInfo.java @@ -520,7 +520,7 @@ public class VolumeInfo implements Parcelable { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (o instanceof VolumeInfo) { return Objects.equals(id, ((VolumeInfo) o).id); } else { diff --git a/core/java/android/os/storage/VolumeRecord.java b/core/java/android/os/storage/VolumeRecord.java index 60df9819f591..0f58a714749b 100644 --- a/core/java/android/os/storage/VolumeRecord.java +++ b/core/java/android/os/storage/VolumeRecord.java @@ -16,6 +16,7 @@ package android.os.storage; +import android.annotation.Nullable; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.os.Environment; @@ -149,7 +150,7 @@ public class VolumeRecord implements Parcelable { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (o instanceof VolumeRecord) { return Objects.equals(fsUuid, ((VolumeRecord) o).fsUuid); } else { diff --git a/core/java/android/permission/PermissionControllerManager.java b/core/java/android/permission/PermissionControllerManager.java index d6c95db95e85..0ba09fdab808 100644 --- a/core/java/android/permission/PermissionControllerManager.java +++ b/core/java/android/permission/PermissionControllerManager.java @@ -79,7 +79,6 @@ import java.util.function.Consumer; * * @hide */ -@TestApi @SystemApi @SystemService(Context.PERMISSION_CONTROLLER_SERVICE) public final class PermissionControllerManager { diff --git a/core/java/android/permission/PermissionManager.java b/core/java/android/permission/PermissionManager.java index d80a7e794220..c319e8580f76 100644 --- a/core/java/android/permission/PermissionManager.java +++ b/core/java/android/permission/PermissionManager.java @@ -24,7 +24,6 @@ import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.annotation.SystemService; -import android.annotation.TestApi; import android.annotation.UserIdInt; import android.app.ActivityManager; import android.app.ActivityThread; @@ -57,7 +56,6 @@ import java.util.function.Consumer; * * @hide */ -@TestApi @SystemApi @SystemService(Context.PERMISSION_SERVICE) public final class PermissionManager { @@ -115,7 +113,6 @@ public final class PermissionManager { * * @hide */ - @TestApi @SystemApi @RequiresPermission(anyOf = { Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY, @@ -136,7 +133,6 @@ public final class PermissionManager { * * @hide */ - @TestApi @SystemApi @RequiresPermission(anyOf = { Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY, @@ -587,7 +583,7 @@ public final class PermissionManager { } @Override - public boolean equals(Object rval) { + public boolean equals(@Nullable Object rval) { // N.B. pid doesn't count toward equality! if (rval == null) { return false; @@ -609,7 +605,7 @@ public final class PermissionManager { /** @hide */ private static final PropertyInvalidatedCache<PermissionQuery, Integer> sPermissionCache = new PropertyInvalidatedCache<PermissionQuery, Integer>( - 16, CACHE_KEY_PACKAGE_INFO, "checkPermission") { + 2048, CACHE_KEY_PACKAGE_INFO, "checkPermission") { @Override protected Integer recompute(PermissionQuery query) { return checkPermissionUncached(query.permission, query.pid, query.uid); @@ -660,7 +656,7 @@ public final class PermissionManager { } @Override - public boolean equals(Object rval) { + public boolean equals(@Nullable Object rval) { if (rval == null) { return false; } diff --git a/core/java/android/permission/RuntimePermissionPresentationInfo.java b/core/java/android/permission/RuntimePermissionPresentationInfo.java index d696feabfc30..4fce14cef3f0 100644 --- a/core/java/android/permission/RuntimePermissionPresentationInfo.java +++ b/core/java/android/permission/RuntimePermissionPresentationInfo.java @@ -18,7 +18,6 @@ package android.permission; import android.annotation.NonNull; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.os.Parcel; import android.os.Parcelable; @@ -33,7 +32,6 @@ import com.android.internal.util.Preconditions; * * @hide */ -@TestApi @SystemApi public final class RuntimePermissionPresentationInfo implements Parcelable { private static final int FLAG_GRANTED = 1 << 0; diff --git a/core/java/android/print/PageRange.java b/core/java/android/print/PageRange.java index 134503874a77..f5f66130a751 100644 --- a/core/java/android/print/PageRange.java +++ b/core/java/android/print/PageRange.java @@ -18,6 +18,7 @@ package android.print; import android.annotation.IntRange; import android.annotation.NonNull; +import android.annotation.Nullable; import android.os.Parcel; import android.os.Parcelable; @@ -128,7 +129,7 @@ public final class PageRange implements Parcelable { } @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { if (this == obj) { return true; } diff --git a/core/java/android/print/PrintAttributes.java b/core/java/android/print/PrintAttributes.java index e607ced63393..934e642cab3b 100644 --- a/core/java/android/print/PrintAttributes.java +++ b/core/java/android/print/PrintAttributes.java @@ -370,7 +370,7 @@ public final class PrintAttributes implements Parcelable { } @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { if (this == obj) { return true; } @@ -1019,7 +1019,7 @@ public final class PrintAttributes implements Parcelable { } @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { if (this == obj) { return true; } @@ -1180,7 +1180,7 @@ public final class PrintAttributes implements Parcelable { } @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { if (this == obj) { return true; } @@ -1304,7 +1304,7 @@ public final class PrintAttributes implements Parcelable { } @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { if (this == obj) { return true; } diff --git a/core/java/android/print/PrintDocumentInfo.java b/core/java/android/print/PrintDocumentInfo.java index b98846163702..a817a7dfa851 100644 --- a/core/java/android/print/PrintDocumentInfo.java +++ b/core/java/android/print/PrintDocumentInfo.java @@ -19,9 +19,11 @@ package android.print; import android.annotation.IntDef; import android.annotation.IntRange; import android.annotation.NonNull; +import android.annotation.Nullable; import android.os.Parcel; import android.os.Parcelable; import android.text.TextUtils; + import com.android.internal.util.Preconditions; import java.lang.annotation.Retention; @@ -235,7 +237,7 @@ public final class PrintDocumentInfo implements Parcelable { } @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { if (this == obj) { return true; } diff --git a/core/java/android/print/PrintJob.java b/core/java/android/print/PrintJob.java index 66181e0f18fa..9c8e61fa4897 100644 --- a/core/java/android/print/PrintJob.java +++ b/core/java/android/print/PrintJob.java @@ -184,7 +184,7 @@ public final class PrintJob { } @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { if (this == obj) { return true; } diff --git a/core/java/android/print/PrintJobId.java b/core/java/android/print/PrintJobId.java index 606cbb819065..35bec5c0c529 100644 --- a/core/java/android/print/PrintJobId.java +++ b/core/java/android/print/PrintJobId.java @@ -17,6 +17,7 @@ package android.print; import android.annotation.NonNull; +import android.annotation.Nullable; import android.os.Parcel; import android.os.Parcelable; @@ -59,7 +60,7 @@ public final class PrintJobId implements Parcelable { } @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { if (this == obj) { return true; } diff --git a/core/java/android/print/PrinterCapabilitiesInfo.java b/core/java/android/print/PrinterCapabilitiesInfo.java index e465d3b6b5de..8f1b864b601a 100644 --- a/core/java/android/print/PrinterCapabilitiesInfo.java +++ b/core/java/android/print/PrinterCapabilitiesInfo.java @@ -17,6 +17,7 @@ package android.print; import android.annotation.NonNull; +import android.annotation.Nullable; import android.os.Parcel; import android.os.Parcelable; import android.print.PrintAttributes.ColorMode; @@ -24,6 +25,7 @@ import android.print.PrintAttributes.DuplexMode; import android.print.PrintAttributes.Margins; import android.print.PrintAttributes.MediaSize; import android.print.PrintAttributes.Resolution; + import com.android.internal.util.Preconditions; import java.util.ArrayList; @@ -271,7 +273,7 @@ public final class PrinterCapabilitiesInfo implements Parcelable { } @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { if (this == obj) { return true; } diff --git a/core/java/android/print/PrinterId.java b/core/java/android/print/PrinterId.java index 75ca7507450f..25260c473709 100644 --- a/core/java/android/print/PrinterId.java +++ b/core/java/android/print/PrinterId.java @@ -17,6 +17,7 @@ package android.print; import android.annotation.NonNull; +import android.annotation.Nullable; import android.compat.annotation.UnsupportedAppUsage; import android.content.ComponentName; import android.os.Parcel; @@ -85,7 +86,7 @@ public final class PrinterId implements Parcelable { } @Override - public boolean equals(Object object) { + public boolean equals(@Nullable Object object) { if (this == object) { return true; } diff --git a/core/java/android/print/PrinterInfo.java b/core/java/android/print/PrinterInfo.java index 4d5ccc0e803e..8e03e3eb3f22 100644 --- a/core/java/android/print/PrinterInfo.java +++ b/core/java/android/print/PrinterInfo.java @@ -360,7 +360,7 @@ public final class PrinterInfo implements Parcelable { } @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { if (this == obj) { return true; } diff --git a/core/java/android/printservice/PrintJob.java b/core/java/android/printservice/PrintJob.java index 3226f343ce66..d0d41e6c68a5 100644 --- a/core/java/android/printservice/PrintJob.java +++ b/core/java/android/printservice/PrintJob.java @@ -27,6 +27,7 @@ import android.print.PrintJobId; import android.print.PrintJobInfo; import android.text.TextUtils; import android.util.Log; + import com.android.internal.util.Preconditions; /** @@ -436,7 +437,7 @@ public final class PrintJob { } @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { if (this == obj) { return true; } diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java index 99ffee3e2ad9..0315b561c120 100644 --- a/core/java/android/provider/DeviceConfig.java +++ b/core/java/android/provider/DeviceConfig.java @@ -55,7 +55,6 @@ import java.util.concurrent.Executor; * @hide */ @SystemApi -@TestApi public final class DeviceConfig { /** * The content:// style URL for the config table. @@ -116,7 +115,6 @@ public final class DeviceConfig { * @hide */ @SystemApi - @TestApi public static final String NAMESPACE_AUTOFILL = "autofill"; /** @@ -150,7 +148,6 @@ public final class DeviceConfig { * @hide */ @SystemApi - @TestApi public static final String NAMESPACE_CONTENT_CAPTURE = "content_capture"; /** @@ -250,7 +247,7 @@ public final class DeviceConfig { * * @hide */ - @SystemApi @TestApi + @SystemApi public static final String NAMESPACE_ROLLBACK = "rollback"; /** @@ -258,7 +255,7 @@ public final class DeviceConfig { * * @hide */ - @SystemApi @TestApi + @SystemApi public static final String NAMESPACE_ROLLBACK_BOOT = "rollback_boot"; /** @@ -403,7 +400,6 @@ public final class DeviceConfig { * @hide */ @SystemApi - @TestApi public static final String NAMESPACE_PRIVACY = "privacy"; /** @@ -412,7 +408,6 @@ public final class DeviceConfig { * @hide */ @SystemApi - @TestApi public static final String NAMESPACE_BIOMETRICS = "biometrics"; /** @@ -421,7 +416,6 @@ public final class DeviceConfig { * @hide */ @SystemApi - @TestApi public static final String NAMESPACE_PERMISSIONS = "permissions"; /** @@ -467,7 +461,6 @@ public final class DeviceConfig { * @hide */ @SystemApi - @TestApi @RequiresPermission(READ_DEVICE_CONFIG) public static String getProperty(@NonNull String namespace, @NonNull String name) { // Fetch all properties for the namespace at once and cache them in the local process, so we @@ -496,7 +489,6 @@ public final class DeviceConfig { * @hide */ @SystemApi - @TestApi @NonNull @RequiresPermission(READ_DEVICE_CONFIG) public static Properties getProperties(@NonNull String namespace, @NonNull String ... names) { @@ -516,7 +508,6 @@ public final class DeviceConfig { * @hide */ @SystemApi - @TestApi @RequiresPermission(READ_DEVICE_CONFIG) public static String getString(@NonNull String namespace, @NonNull String name, @Nullable String defaultValue) { @@ -535,7 +526,6 @@ public final class DeviceConfig { * @hide */ @SystemApi - @TestApi @RequiresPermission(READ_DEVICE_CONFIG) public static boolean getBoolean(@NonNull String namespace, @NonNull String name, boolean defaultValue) { @@ -554,7 +544,6 @@ public final class DeviceConfig { * @hide */ @SystemApi - @TestApi @RequiresPermission(READ_DEVICE_CONFIG) public static int getInt(@NonNull String namespace, @NonNull String name, int defaultValue) { String value = getProperty(namespace, name); @@ -580,7 +569,6 @@ public final class DeviceConfig { * @hide */ @SystemApi - @TestApi @RequiresPermission(READ_DEVICE_CONFIG) public static long getLong(@NonNull String namespace, @NonNull String name, long defaultValue) { String value = getProperty(namespace, name); @@ -606,7 +594,6 @@ public final class DeviceConfig { * @hide */ @SystemApi - @TestApi @RequiresPermission(READ_DEVICE_CONFIG) public static float getFloat(@NonNull String namespace, @NonNull String name, float defaultValue) { @@ -642,7 +629,6 @@ public final class DeviceConfig { * @see #resetToDefaults(int, String). */ @SystemApi - @TestApi @RequiresPermission(WRITE_DEVICE_CONFIG) public static boolean setProperty(@NonNull String namespace, @NonNull String name, @Nullable String value, boolean makeDefault) { @@ -666,7 +652,6 @@ public final class DeviceConfig { * @hide */ @SystemApi - @TestApi @RequiresPermission(WRITE_DEVICE_CONFIG) public static boolean setProperties(@NonNull Properties properties) throws BadConfigException { ContentResolver contentResolver = ActivityThread.currentApplication().getContentResolver(); @@ -686,7 +671,6 @@ public final class DeviceConfig { * @see #setProperty(String, String, String, boolean) */ @SystemApi - @TestApi @RequiresPermission(WRITE_DEVICE_CONFIG) public static void resetToDefaults(@ResetMode int resetMode, @Nullable String namespace) { ContentResolver contentResolver = ActivityThread.currentApplication().getContentResolver(); @@ -708,7 +692,6 @@ public final class DeviceConfig { * @see #removeOnPropertiesChangedListener(OnPropertiesChangedListener) */ @SystemApi - @TestApi @RequiresPermission(READ_DEVICE_CONFIG) public static void addOnPropertiesChangedListener( @NonNull String namespace, @@ -743,7 +726,6 @@ public final class DeviceConfig { * @see #addOnPropertiesChangedListener(String, Executor, OnPropertiesChangedListener) */ @SystemApi - @TestApi public static void removeOnPropertiesChangedListener( @NonNull OnPropertiesChangedListener onPropertiesChangedListener) { Preconditions.checkNotNull(onPropertiesChangedListener); @@ -878,7 +860,6 @@ public final class DeviceConfig { * @hide */ @SystemApi - @TestApi public interface OnPropertiesChangedListener { /** * Called when one or more properties have changed, providing a Properties object with all @@ -899,7 +880,6 @@ public final class DeviceConfig { * @hide */ @SystemApi - @TestApi public static class BadConfigException extends Exception {} /** @@ -908,7 +888,6 @@ public final class DeviceConfig { * @hide */ @SystemApi - @TestApi public static class Properties { private final String mNamespace; private final HashMap<String, String> mMap; diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java index da9794d409eb..062d92900643 100644 --- a/core/java/android/provider/DocumentsContract.java +++ b/core/java/android/provider/DocumentsContract.java @@ -22,7 +22,6 @@ import static com.android.internal.util.Preconditions.checkCollectionNotEmpty; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.compat.annotation.UnsupportedAppUsage; import android.content.ContentProvider; import android.content.ContentResolver; @@ -1304,7 +1303,6 @@ public final class DocumentsContract { * {@hide} */ @SystemApi - @TestApi public static @NonNull Uri setManageMode(@NonNull Uri uri) { Preconditions.checkNotNull(uri, "uri can not be null"); return uri.buildUpon().appendQueryParameter(PARAM_MANAGE, "true").build(); @@ -1316,7 +1314,6 @@ public final class DocumentsContract { * {@hide} */ @SystemApi - @TestApi public static boolean isManageMode(@NonNull Uri uri) { Preconditions.checkNotNull(uri, "uri can not be null"); return uri.getBooleanQueryParameter(PARAM_MANAGE, false); @@ -1811,7 +1808,7 @@ public final class DocumentsContract { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (this == o) { return true; } diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 8ad76692db04..0e3708e04dd9 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -229,7 +229,6 @@ public final class Settings { */ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) @SystemApi - @TestApi public static final String ACTION_TETHER_PROVISIONING_UI = "android.settings.TETHER_PROVISIONING_UI"; @@ -1053,7 +1052,6 @@ public final class Settings { * * @hide */ - @TestApi @SystemApi @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String ACTION_MANAGE_APP_OVERLAY_PERMISSION = @@ -2017,7 +2015,6 @@ public final class Settings { * @hide */ @SystemApi - @TestApi @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String ACTION_ENTERPRISE_PRIVACY_SETTINGS = "android.settings.ENTERPRISE_PRIVACY_SETTINGS"; @@ -2100,7 +2097,6 @@ public final class Settings { * @hide */ @SystemApi - @TestApi @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String ACTION_REQUEST_ENABLE_CONTENT_CAPTURE = "android.settings.REQUEST_ENABLE_CONTENT_CAPTURE"; @@ -5580,7 +5576,6 @@ public final class Settings { * @hide */ @SystemApi - @TestApi @RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS) public static void resetToDefaults(@NonNull ContentResolver resolver, @Nullable String tag) { @@ -6051,7 +6046,6 @@ public final class Settings { * @hide */ @SystemApi - @TestApi public static final String AUTOFILL_FEATURE_FIELD_CLASSIFICATION = "autofill_field_classification"; @@ -6085,7 +6079,6 @@ public final class Settings { * @hide */ @SystemApi - @TestApi public static final String AUTOFILL_USER_DATA_MAX_USER_DATA_SIZE = "autofill_user_data_max_user_data_size"; @@ -6096,7 +6089,6 @@ public final class Settings { * @hide */ @SystemApi - @TestApi public static final String AUTOFILL_USER_DATA_MAX_FIELD_CLASSIFICATION_IDS_SIZE = "autofill_user_data_max_field_classification_size"; @@ -6107,7 +6099,6 @@ public final class Settings { * @hide */ @SystemApi - @TestApi public static final String AUTOFILL_USER_DATA_MAX_CATEGORY_COUNT = "autofill_user_data_max_category_count"; @@ -6117,7 +6108,6 @@ public final class Settings { * @hide */ @SystemApi - @TestApi public static final String AUTOFILL_USER_DATA_MAX_VALUE_LENGTH = "autofill_user_data_max_value_length"; @@ -6127,7 +6117,6 @@ public final class Settings { * @hide */ @SystemApi - @TestApi public static final String AUTOFILL_USER_DATA_MIN_VALUE_LENGTH = "autofill_user_data_min_value_length"; @@ -6177,7 +6166,6 @@ public final class Settings { * @hide */ @SystemApi - @TestApi public static final String USER_SETUP_COMPLETE = "user_setup_complete"; /** @@ -6553,7 +6541,6 @@ public final class Settings { * @hide */ @SystemApi - @TestApi public static final String LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS = "lock_screen_allow_private_notifications"; @@ -7841,7 +7828,6 @@ public final class Settings { * @hide */ @SystemApi - @TestApi public static final String DOZE_ALWAYS_ON = "doze_always_on"; /** @@ -8274,7 +8260,6 @@ public final class Settings { * @hide */ @SystemApi - @TestApi public static final String LOCK_SCREEN_SHOW_NOTIFICATIONS = "lock_screen_show_notifications"; @@ -8961,7 +8946,6 @@ public final class Settings { * @hide */ @SystemApi - @TestApi public static final String LOCATION_ACCESS_CHECK_INTERVAL_MILLIS = "location_access_check_interval_millis"; @@ -8970,7 +8954,6 @@ public final class Settings { * @hide */ @SystemApi - @TestApi public static final String LOCATION_ACCESS_CHECK_DELAY_MILLIS = "location_access_check_delay_millis"; @@ -9128,6 +9111,15 @@ public final class Settings { "accessibility_magnification_capability"; /** + * Whether to show the window magnification prompt dialog when the user uses full-screen + * magnification first time after database is upgraded . + * + * @hide + */ + public static final String ACCESSIBILITY_SHOW_WINDOW_MAGNIFICATION_PROMPT = + "accessibility_show_window_magnification_prompt"; + + /** * Whether the Adaptive connectivity option is enabled. * * @hide @@ -10382,7 +10374,6 @@ public final class Settings { * @hide */ @SystemApi - @TestApi public static final String TETHER_OFFLOAD_DISABLED = "tether_offload_disabled"; /** @@ -12358,37 +12349,34 @@ public final class Settings { * to dumpable apps that opt-in. * @hide */ - public static final String GLOBAL_SETTINGS_ANGLE_DEBUG_PACKAGE = - "angle_debug_package"; + public static final String ANGLE_DEBUG_PACKAGE = "angle_debug_package"; /** * Force all PKGs to use ANGLE, regardless of any other settings * The value is a boolean (1 or 0). * @hide */ - public static final String GLOBAL_SETTINGS_ANGLE_GL_DRIVER_ALL_ANGLE = - "angle_gl_driver_all_angle"; + public static final String ANGLE_GL_DRIVER_ALL_ANGLE = "angle_gl_driver_all_angle"; /** * List of PKGs that have an OpenGL driver selected * @hide */ - public static final String GLOBAL_SETTINGS_ANGLE_GL_DRIVER_SELECTION_PKGS = + public static final String ANGLE_GL_DRIVER_SELECTION_PKGS = "angle_gl_driver_selection_pkgs"; /** * List of selected OpenGL drivers, corresponding to the PKGs in GLOBAL_SETTINGS_DRIVER_PKGS * @hide */ - public static final String GLOBAL_SETTINGS_ANGLE_GL_DRIVER_SELECTION_VALUES = + public static final String ANGLE_GL_DRIVER_SELECTION_VALUES = "angle_gl_driver_selection_values"; /** * List of package names that should check ANGLE rules * @hide */ - public static final String GLOBAL_SETTINGS_ANGLE_ALLOWLIST = - "angle_allowlist"; + public static final String ANGLE_ALLOWLIST = "angle_allowlist"; /** * Lists of ANGLE EGL features for debugging. @@ -12404,8 +12392,7 @@ public final class Settings { * The value is a boolean (1 or 0). * @hide */ - public static final String GLOBAL_SETTINGS_SHOW_ANGLE_IN_USE_DIALOG_BOX = - "show_angle_in_use_dialog_box"; + public static final String SHOW_ANGLE_IN_USE_DIALOG_BOX = "show_angle_in_use_dialog_box"; /** * Updatable driver global preference for all Apps. @@ -13364,7 +13351,6 @@ public final class Settings { * @hide */ @SystemApi - @TestApi public static final String AUTOFILL_COMPAT_MODE_ALLOWED_PACKAGES = "autofill_compat_mode_allowed_packages"; @@ -13412,6 +13398,7 @@ public final class Settings { * * @hide */ + @TestApi public static final String HIDDEN_API_POLICY = "hidden_api_policy"; /** @@ -14541,6 +14528,16 @@ public final class Settings { public static final String SHOW_PEOPLE_SPACE = "show_people_space"; /** + * Which types of conversations to show in People Space. + * Values are: + * 0: All conversations (default) + * 1: Priority conversations only + * @hide + */ + public static final String PEOPLE_SPACE_CONVERSATION_TYPE = + "people_space_conversation_type"; + + /** * Whether to show new lockscreen & AOD UI. * Values are: * 0: Disabled (default) @@ -14590,6 +14587,19 @@ public final class Settings { */ public static final String MAXIMUM_OBSCURING_OPACITY_FOR_TOUCH = "maximum_obscuring_opacity_for_touch"; + + /** + * LatencyTracker settings. + * + * The following strings are supported as keys: + * <pre> + * enabled (boolean) + * sampling_interval (int) + * </pre> + * + * @hide + */ + public static final String LATENCY_TRACKER = "latency_tracker"; } /** diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java index 79d6bb4a062a..2c735fd9012f 100644 --- a/core/java/android/provider/Telephony.java +++ b/core/java/android/provider/Telephony.java @@ -4045,7 +4045,6 @@ public final class Telephony { * @hide */ @SystemApi - @TestApi public static final class CellBroadcasts implements BaseColumns { /** diff --git a/core/java/android/security/net/config/Domain.java b/core/java/android/security/net/config/Domain.java index 5bb727a38033..c87c173ccc4d 100644 --- a/core/java/android/security/net/config/Domain.java +++ b/core/java/android/security/net/config/Domain.java @@ -16,7 +16,10 @@ package android.security.net.config; +import android.annotation.Nullable; + import java.util.Locale; + /** @hide */ public final class Domain { /** @@ -43,7 +46,7 @@ public final class Domain { } @Override - public boolean equals(Object other) { + public boolean equals(@Nullable Object other) { if (other == this) { return true; } diff --git a/core/java/android/security/net/config/Pin.java b/core/java/android/security/net/config/Pin.java index 94520e22bd02..7ac0ab07ff93 100644 --- a/core/java/android/security/net/config/Pin.java +++ b/core/java/android/security/net/config/Pin.java @@ -16,6 +16,8 @@ package android.security.net.config; +import android.annotation.Nullable; + import java.util.Arrays; /** @hide */ @@ -56,7 +58,7 @@ public final class Pin { } @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { if (this == obj) { return true; } diff --git a/core/java/android/service/appprediction/AppPredictionService.java b/core/java/android/service/appprediction/AppPredictionService.java index be20570ef62d..2d8aee567010 100644 --- a/core/java/android/service/appprediction/AppPredictionService.java +++ b/core/java/android/service/appprediction/AppPredictionService.java @@ -22,7 +22,6 @@ import android.annotation.MainThread; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.app.Service; import android.app.prediction.AppPredictionContext; import android.app.prediction.AppPredictionSessionId; @@ -52,7 +51,6 @@ import java.util.function.Consumer; * @hide */ @SystemApi -@TestApi public abstract class AppPredictionService extends Service { private static final String TAG = "AppPredictionService"; diff --git a/core/java/android/service/autofill/AutofillFieldClassificationService.java b/core/java/android/service/autofill/AutofillFieldClassificationService.java index 28842a7fa1d7..9d9b881ebdfc 100644 --- a/core/java/android/service/autofill/AutofillFieldClassificationService.java +++ b/core/java/android/service/autofill/AutofillFieldClassificationService.java @@ -20,7 +20,6 @@ import static com.android.internal.util.function.pooled.PooledLambda.obtainMessa import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.app.Service; import android.content.Intent; import android.os.Bundle; @@ -53,7 +52,6 @@ import java.util.Map; * {@hide} */ @SystemApi -@TestApi public abstract class AutofillFieldClassificationService extends Service { private static final String TAG = "AutofillFieldClassificationService"; @@ -121,7 +119,6 @@ public abstract class AutofillFieldClassificationService extends Service { /** @hide */ @SystemApi - @TestApi public AutofillFieldClassificationService() { } diff --git a/core/java/android/service/autofill/Dataset.java b/core/java/android/service/autofill/Dataset.java index 2d99c413cc89..18d79927388b 100644 --- a/core/java/android/service/autofill/Dataset.java +++ b/core/java/android/service/autofill/Dataset.java @@ -21,7 +21,6 @@ import static android.view.autofill.Helper.sDebug; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.content.IntentSender; import android.os.Parcel; import android.os.Parcelable; @@ -249,7 +248,6 @@ public final class Dataset implements Parcelable { * @hide */ @SystemApi - @TestApi public Builder(@NonNull InlinePresentation inlinePresentation) { Preconditions.checkNotNull(inlinePresentation, "inlinePresentation must be non-null"); mInlinePresentation = inlinePresentation; @@ -604,7 +602,6 @@ public final class Dataset implements Parcelable { * @hide */ @SystemApi - @TestApi public @NonNull Builder setFieldInlinePresentation(@NonNull AutofillId id, @Nullable AutofillValue value, @Nullable Pattern filter, @NonNull InlinePresentation inlinePresentation) { diff --git a/core/java/android/service/autofill/FillEventHistory.java b/core/java/android/service/autofill/FillEventHistory.java index 1cd2d62ce55f..f2265281b140 100644 --- a/core/java/android/service/autofill/FillEventHistory.java +++ b/core/java/android/service/autofill/FillEventHistory.java @@ -159,6 +159,7 @@ public final class FillEventHistory implements Parcelable { FieldClassification.writeArrayToParcel(parcel, event.mDetectedFieldClassifications); } + parcel.writeInt(event.mSaveDialogNotShowReason); } } } @@ -243,6 +244,40 @@ public final class FillEventHistory implements Parcelable { @Retention(RetentionPolicy.SOURCE) @interface EventIds{} + /** No reason for save dialog. */ + public static final int NO_SAVE_REASON_NONE = 0; + + /** The SaveInfo associated with the FillResponse is null. */ + public static final int NO_SAVE_REASON_NO_SAVE_INFO = 1; + + /** The service asked to delay save. */ + public static final int NO_SAVE_REASON_WITH_DELAY_SAVE_FLAG = 2; + + /** There was empty value for required ids. */ + public static final int NO_SAVE_REASON_HAS_EMPTY_REQUIRED = 3; + + /** No value has been changed. */ + public static final int NO_SAVE_REASON_NO_VALUE_CHANGED = 4; + + /** Fields failed validation. */ + public static final int NO_SAVE_REASON_FIELD_VALIDATION_FAILED = 5; + + /** All fields matched contents of datasets. */ + public static final int NO_SAVE_REASON_DATASET_MATCH = 6; + + /** @hide */ + @IntDef(prefix = { "NO_SAVE_REASON_" }, value = { + NO_SAVE_REASON_NONE, + NO_SAVE_REASON_NO_SAVE_INFO, + NO_SAVE_REASON_WITH_DELAY_SAVE_FLAG, + NO_SAVE_REASON_HAS_EMPTY_REQUIRED, + NO_SAVE_REASON_NO_VALUE_CHANGED, + NO_SAVE_REASON_FIELD_VALIDATION_FAILED, + NO_SAVE_REASON_DATASET_MATCH + }) + @Retention(RetentionPolicy.SOURCE) + public @interface NoSaveReason{} + @EventIds private final int mEventType; @Nullable private final String mDatasetId; @Nullable private final Bundle mClientState; @@ -261,6 +296,8 @@ public final class FillEventHistory implements Parcelable { @Nullable private final AutofillId[] mDetectedFieldIds; @Nullable private final FieldClassification[] mDetectedFieldClassifications; + @NoSaveReason private final int mSaveDialogNotShowReason; + /** * Returns the type of the event. * @@ -448,6 +485,18 @@ public final class FillEventHistory implements Parcelable { } /** + * Returns the reason why a save dialog was not shown. + * + * <p><b>Note: </b>Only set on events of type {@link #TYPE_CONTEXT_COMMITTED}. For the other + * event types, the reason is set to NO_SAVE_REASON_NONE. + * + * @return The reason why a save dialog was not shown. + */ + public int getNoSaveReason() { + return mSaveDialogNotShowReason; + } + + /** * Creates a new event. * * @param eventType The type of the event @@ -481,6 +530,48 @@ public final class FillEventHistory implements Parcelable { @Nullable ArrayList<ArrayList<String>> manuallyFilledDatasetIds, @Nullable AutofillId[] detectedFieldIds, @Nullable FieldClassification[] detectedFieldClassifications) { + this(eventType, datasetId, clientState, selectedDatasetIds, ignoredDatasetIds, + changedFieldIds, changedDatasetIds, manuallyFilledFieldIds, + manuallyFilledDatasetIds, detectedFieldIds, detectedFieldClassifications, + NO_SAVE_REASON_NONE); + } + + /** + * Creates a new event. + * + * @param eventType The type of the event + * @param datasetId The dataset the event was on, or {@code null} if the event was on the + * whole response. + * @param clientState The client state associated with the event. + * @param selectedDatasetIds The ids of datasets selected by the user. + * @param ignoredDatasetIds The ids of datasets NOT select by the user. + * @param changedFieldIds The ids of fields changed by the user. + * @param changedDatasetIds The ids of the datasets that havd values matching the + * respective entry on {@code changedFieldIds}. + * @param manuallyFilledFieldIds The ids of fields that were manually entered by the user + * and belonged to datasets. + * @param manuallyFilledDatasetIds The ids of datasets that had values matching the + * respective entry on {@code manuallyFilledFieldIds}. + * @param detectedFieldClassifications the field classification matches. + * @param saveDialogNotShowReason The reason why a save dialog was not shown. + * + * @throws IllegalArgumentException If the length of {@code changedFieldIds} and + * {@code changedDatasetIds} doesn't match. + * @throws IllegalArgumentException If the length of {@code manuallyFilledFieldIds} and + * {@code manuallyFilledDatasetIds} doesn't match. + * + * @hide + */ + public Event(int eventType, @Nullable String datasetId, @Nullable Bundle clientState, + @Nullable List<String> selectedDatasetIds, + @Nullable ArraySet<String> ignoredDatasetIds, + @Nullable ArrayList<AutofillId> changedFieldIds, + @Nullable ArrayList<String> changedDatasetIds, + @Nullable ArrayList<AutofillId> manuallyFilledFieldIds, + @Nullable ArrayList<ArrayList<String>> manuallyFilledDatasetIds, + @Nullable AutofillId[] detectedFieldIds, + @Nullable FieldClassification[] detectedFieldClassifications, + int saveDialogNotShowReason) { mEventType = Preconditions.checkArgumentInRange(eventType, 0, TYPE_DATASETS_SHOWN, "eventType"); mDatasetId = datasetId; @@ -506,6 +597,10 @@ public final class FillEventHistory implements Parcelable { mDetectedFieldIds = detectedFieldIds; mDetectedFieldClassifications = detectedFieldClassifications; + + mSaveDialogNotShowReason = Preconditions.checkArgumentInRange(saveDialogNotShowReason, + NO_SAVE_REASON_NONE, NO_SAVE_REASON_DATASET_MATCH, + "saveDialogNotShowReason"); } @Override @@ -521,6 +616,7 @@ public final class FillEventHistory implements Parcelable { + ", detectedFieldIds=" + Arrays.toString(mDetectedFieldIds) + ", detectedFieldClassifications =" + Arrays.toString(mDetectedFieldClassifications) + + ", saveDialogNotShowReason=" + mSaveDialogNotShowReason + "]"; } } @@ -562,12 +658,14 @@ public final class FillEventHistory implements Parcelable { (detectedFieldIds != null) ? FieldClassification.readArrayFromParcel(parcel) : null; + final int saveDialogNotShowReason = parcel.readInt(); selection.addEvent(new Event(eventType, datasetId, clientState, selectedDatasetIds, ignoredDatasets, changedFieldIds, changedDatasetIds, manuallyFilledFieldIds, manuallyFilledDatasetIds, - detectedFieldIds, detectedFieldClassifications)); + detectedFieldIds, detectedFieldClassifications, + saveDialogNotShowReason)); } return selection; } diff --git a/core/java/android/service/autofill/InlineSuggestionRenderService.java b/core/java/android/service/autofill/InlineSuggestionRenderService.java index 839caff5c3d4..cdcd65910974 100644 --- a/core/java/android/service/autofill/InlineSuggestionRenderService.java +++ b/core/java/android/service/autofill/InlineSuggestionRenderService.java @@ -20,7 +20,6 @@ import static com.android.internal.util.function.pooled.PooledLambda.obtainMessa import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.app.Service; import android.content.Intent; import android.content.IntentSender; @@ -51,7 +50,6 @@ import java.lang.ref.WeakReference; * {@hide} */ @SystemApi -@TestApi public abstract class InlineSuggestionRenderService extends Service { private static final String TAG = "InlineSuggestionRenderService"; diff --git a/core/java/android/service/autofill/augmented/AugmentedAutofillService.java b/core/java/android/service/autofill/augmented/AugmentedAutofillService.java index 620c457024b8..b34c2dceeee8 100644 --- a/core/java/android/service/autofill/augmented/AugmentedAutofillService.java +++ b/core/java/android/service/autofill/augmented/AugmentedAutofillService.java @@ -25,7 +25,6 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.app.Service; import android.content.ComponentName; import android.content.Intent; @@ -70,7 +69,6 @@ import java.util.List; * @hide */ @SystemApi -@TestApi public abstract class AugmentedAutofillService extends Service { private static final String TAG = AugmentedAutofillService.class.getSimpleName(); diff --git a/core/java/android/service/autofill/augmented/FillCallback.java b/core/java/android/service/autofill/augmented/FillCallback.java index fc3baf1c9836..9fc7f34ec0ef 100644 --- a/core/java/android/service/autofill/augmented/FillCallback.java +++ b/core/java/android/service/autofill/augmented/FillCallback.java @@ -20,7 +20,6 @@ import static android.service.autofill.augmented.AugmentedAutofillService.sDebug import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.os.Bundle; import android.service.autofill.Dataset; import android.service.autofill.augmented.AugmentedAutofillService.AutofillProxy; @@ -34,7 +33,6 @@ import java.util.List; * @hide */ @SystemApi -@TestApi public final class FillCallback { private static final String TAG = FillCallback.class.getSimpleName(); diff --git a/core/java/android/service/autofill/augmented/FillController.java b/core/java/android/service/autofill/augmented/FillController.java index 7d552d62fa72..7cd674e847fd 100644 --- a/core/java/android/service/autofill/augmented/FillController.java +++ b/core/java/android/service/autofill/augmented/FillController.java @@ -19,7 +19,6 @@ import static android.service.autofill.augmented.AugmentedAutofillService.sDebug import android.annotation.NonNull; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.os.RemoteException; import android.service.autofill.augmented.AugmentedAutofillService.AutofillProxy; import android.util.Log; @@ -37,7 +36,6 @@ import java.util.List; * @hide */ @SystemApi -@TestApi public final class FillController { private static final String TAG = FillController.class.getSimpleName(); diff --git a/core/java/android/service/autofill/augmented/FillResponse.java b/core/java/android/service/autofill/augmented/FillResponse.java index f72eb782c407..53484cf200c4 100644 --- a/core/java/android/service/autofill/augmented/FillResponse.java +++ b/core/java/android/service/autofill/augmented/FillResponse.java @@ -18,7 +18,6 @@ package android.service.autofill.augmented; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.os.Bundle; import android.service.autofill.Dataset; @@ -33,7 +32,6 @@ import java.util.List; * @hide */ @SystemApi -@TestApi @DataClass( genBuilder = true, genHiddenGetters = true) diff --git a/core/java/android/service/autofill/augmented/FillWindow.java b/core/java/android/service/autofill/augmented/FillWindow.java index 8e866466e8df..d4f7e114c291 100644 --- a/core/java/android/service/autofill/augmented/FillWindow.java +++ b/core/java/android/service/autofill/augmented/FillWindow.java @@ -23,7 +23,6 @@ import static com.android.internal.util.function.pooled.PooledLambda.obtainMessa import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.graphics.Rect; import android.os.Handler; import android.os.Looper; @@ -64,7 +63,6 @@ import java.lang.ref.WeakReference; * @hide */ @SystemApi -@TestApi public final class FillWindow implements AutoCloseable { private static final String TAG = FillWindow.class.getSimpleName(); diff --git a/core/java/android/service/autofill/augmented/PresentationParams.java b/core/java/android/service/autofill/augmented/PresentationParams.java index 8b3a001f58e6..fe78d2218266 100644 --- a/core/java/android/service/autofill/augmented/PresentationParams.java +++ b/core/java/android/service/autofill/augmented/PresentationParams.java @@ -18,7 +18,6 @@ package android.service.autofill.augmented; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.graphics.Rect; import android.service.autofill.augmented.AugmentedAutofillService.AutofillProxy; import android.view.View; @@ -36,7 +35,6 @@ import java.io.PrintWriter; * @hide */ @SystemApi -@TestApi public abstract class PresentationParams { // /** @hide */ @@ -61,7 +59,6 @@ public abstract class PresentationParams { * @hide */ @SystemApi - @TestApi public abstract static class Area { /** @hide */ diff --git a/core/java/android/service/contentcapture/ActivityEvent.java b/core/java/android/service/contentcapture/ActivityEvent.java index b741cff9328e..1188a3f1021e 100644 --- a/core/java/android/service/contentcapture/ActivityEvent.java +++ b/core/java/android/service/contentcapture/ActivityEvent.java @@ -19,7 +19,6 @@ package android.service.contentcapture; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.app.usage.UsageEvents.Event; import android.content.ComponentName; import android.os.Parcel; @@ -34,7 +33,6 @@ import java.lang.annotation.RetentionPolicy; * @hide */ @SystemApi -@TestApi public final class ActivityEvent implements Parcelable { /** diff --git a/core/java/android/service/contentcapture/ContentCaptureService.java b/core/java/android/service/contentcapture/ContentCaptureService.java index 84f602820f4a..3c44cfd4c7f6 100644 --- a/core/java/android/service/contentcapture/ContentCaptureService.java +++ b/core/java/android/service/contentcapture/ContentCaptureService.java @@ -26,7 +26,6 @@ import android.annotation.CallSuper; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.app.Service; import android.content.ComponentName; import android.content.ContentCaptureOptions; @@ -74,7 +73,6 @@ import java.util.function.Consumer; * @hide */ @SystemApi -@TestApi public abstract class ContentCaptureService extends Service { private static final String TAG = ContentCaptureService.class.getSimpleName(); @@ -344,7 +342,6 @@ public abstract class ContentCaptureService extends Service { * @hide */ @SystemApi - @TestApi public void onDataShareRequest(@NonNull DataShareRequest request, @NonNull DataShareCallback callback) { if (sVerbose) Log.v(TAG, "onDataShareRequest()"); diff --git a/core/java/android/service/contentcapture/DataShareCallback.java b/core/java/android/service/contentcapture/DataShareCallback.java index 5df8a4b174bf..e3c7bb3cd24f 100644 --- a/core/java/android/service/contentcapture/DataShareCallback.java +++ b/core/java/android/service/contentcapture/DataShareCallback.java @@ -19,7 +19,6 @@ package android.service.contentcapture; import android.annotation.CallbackExecutor; import android.annotation.NonNull; import android.annotation.SystemApi; -import android.annotation.TestApi; import java.util.concurrent.Executor; @@ -33,7 +32,6 @@ import java.util.concurrent.Executor; * @hide **/ @SystemApi -@TestApi public interface DataShareCallback { /** Accept the data share. diff --git a/core/java/android/service/contentcapture/DataShareReadAdapter.java b/core/java/android/service/contentcapture/DataShareReadAdapter.java index 8cd9eea1e6e0..4526aba6933b 100644 --- a/core/java/android/service/contentcapture/DataShareReadAdapter.java +++ b/core/java/android/service/contentcapture/DataShareReadAdapter.java @@ -18,7 +18,6 @@ package android.service.contentcapture; import android.annotation.NonNull; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.os.ParcelFileDescriptor; import android.view.contentcapture.ContentCaptureManager.DataShareError; @@ -29,7 +28,6 @@ import android.view.contentcapture.ContentCaptureManager.DataShareError; * @hide **/ @SystemApi -@TestApi public interface DataShareReadAdapter { /** diff --git a/core/java/android/service/contentcapture/SnapshotData.java b/core/java/android/service/contentcapture/SnapshotData.java index 5b3930ab6336..bf469b4b3ad8 100644 --- a/core/java/android/service/contentcapture/SnapshotData.java +++ b/core/java/android/service/contentcapture/SnapshotData.java @@ -19,7 +19,6 @@ package android.service.contentcapture; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.app.assist.AssistContent; import android.app.assist.AssistStructure; import android.os.Bundle; @@ -32,7 +31,6 @@ import android.os.Parcelable; * @hide */ @SystemApi -@TestApi public final class SnapshotData implements Parcelable { private final @NonNull Bundle mAssistData; diff --git a/core/java/android/service/dreams/DreamActivity.java b/core/java/android/service/dreams/DreamActivity.java index 09d1bb9d2b12..96bbf8ebc2cb 100644 --- a/core/java/android/service/dreams/DreamActivity.java +++ b/core/java/android/service/dreams/DreamActivity.java @@ -19,7 +19,6 @@ package android.service.dreams; import android.annotation.Nullable; import android.app.Activity; import android.os.Bundle; -import android.view.WindowInsets; import com.android.internal.R; @@ -63,8 +62,6 @@ public class DreamActivity extends Activity { @Override public void onResume() { super.onResume(); - // Hide all insets (nav bar, status bar, etc) when the dream is showing - getWindow().getInsetsController().hide(WindowInsets.Type.systemBars()); overridePendingTransition(R.anim.dream_activity_open_enter, R.anim.dream_activity_open_exit); } diff --git a/core/java/android/service/dreams/DreamService.java b/core/java/android/service/dreams/DreamService.java index d2dfb29ba25c..859bb51607b9 100644 --- a/core/java/android/service/dreams/DreamService.java +++ b/core/java/android/service/dreams/DreamService.java @@ -48,6 +48,7 @@ import android.view.SearchEvent; import android.view.View; import android.view.ViewGroup; import android.view.Window; +import android.view.WindowInsets; import android.view.WindowManager; import android.view.WindowManager.LayoutParams; import android.view.accessibility.AccessibilityEvent; @@ -1061,11 +1062,17 @@ public class DreamService extends Service implements Window.Callback { | (mFullscreen ? WindowManager.LayoutParams.FLAG_FULLSCREEN : 0) | (mScreenBright ? WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON : 0) ); + lp.layoutInDisplayCutoutMode = + WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; mWindow.setAttributes(lp); // Workaround: Currently low-profile and in-window system bar backgrounds don't go // along well. Dreams usually don't need such bars anyways, so disable them by default. mWindow.clearFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); + // Hide all insets when the dream is showing + mWindow.getDecorView().getWindowInsetsController().hide(WindowInsets.Type.systemBars()); + mWindow.setDecorFitsSystemWindows(false); + mWindow.getDecorView().addOnAttachStateChangeListener( new View.OnAttachStateChangeListener() { @Override diff --git a/core/java/android/service/notification/Adjustment.java b/core/java/android/service/notification/Adjustment.java index 8464c6df4425..4b25c8832068 100644 --- a/core/java/android/service/notification/Adjustment.java +++ b/core/java/android/service/notification/Adjustment.java @@ -19,7 +19,6 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.StringDef; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.app.Notification; import android.os.Bundle; import android.os.Parcel; @@ -42,7 +41,6 @@ import java.lang.annotation.RetentionPolicy; * @hide */ @SystemApi -@TestApi public final class Adjustment implements Parcelable { private final String mPackage; private final String mKey; @@ -148,7 +146,6 @@ public final class Adjustment implements Parcelable { * @hide */ @SystemApi - @TestApi public Adjustment(String pkg, String key, Bundle signals, CharSequence explanation, int user) { mPackage = pkg; mKey = key; @@ -232,7 +229,6 @@ public final class Adjustment implements Parcelable { /** @hide */ @SystemApi - @TestApi public int getUser() { return mUser; } diff --git a/core/java/android/service/notification/Condition.java b/core/java/android/service/notification/Condition.java index cf57e2590c9c..4f324f9e35bf 100644 --- a/core/java/android/service/notification/Condition.java +++ b/core/java/android/service/notification/Condition.java @@ -17,6 +17,7 @@ package android.service.notification; import android.annotation.IntDef; +import android.annotation.Nullable; import android.content.Context; import android.net.Uri; import android.os.Parcel; @@ -183,7 +184,7 @@ public final class Condition implements Parcelable { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (!(o instanceof Condition)) return false; if (o == this) return true; final Condition other = (Condition) o; diff --git a/core/java/android/service/notification/ConversationChannelWrapper.java b/core/java/android/service/notification/ConversationChannelWrapper.java index ab465ab74ee5..3d0984ca80ee 100644 --- a/core/java/android/service/notification/ConversationChannelWrapper.java +++ b/core/java/android/service/notification/ConversationChannelWrapper.java @@ -16,6 +16,7 @@ package android.service.notification; +import android.annotation.Nullable; import android.app.NotificationChannel; import android.content.pm.ShortcutInfo; import android.graphics.drawable.Drawable; @@ -126,7 +127,7 @@ public final class ConversationChannelWrapper implements Parcelable { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; ConversationChannelWrapper that = (ConversationChannelWrapper) o; diff --git a/core/java/android/service/notification/NotificationAssistantService.java b/core/java/android/service/notification/NotificationAssistantService.java index 975e75ccaeeb..632014936425 100644 --- a/core/java/android/service/notification/NotificationAssistantService.java +++ b/core/java/android/service/notification/NotificationAssistantService.java @@ -23,7 +23,6 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SdkConstant; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.app.Notification; import android.app.NotificationChannel; import android.app.NotificationManager; @@ -66,7 +65,6 @@ import java.util.List; * @hide */ @SystemApi -@TestApi public abstract class NotificationAssistantService extends NotificationListenerService { private static final String TAG = "NotificationAssistants"; diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java index 8f119fc25fd5..25f140f29e00 100644 --- a/core/java/android/service/notification/NotificationListenerService.java +++ b/core/java/android/service/notification/NotificationListenerService.java @@ -22,7 +22,6 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SdkConstant; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.app.ActivityManager; import android.app.INotificationManager; import android.app.Notification; @@ -452,7 +451,6 @@ public abstract class NotificationListenerService extends Service { * * @hide */ - @TestApi @SystemApi public void onNotificationRemoved(@NonNull StatusBarNotification sbn, @NonNull RankingMap rankingMap, @NonNull NotificationStats stats, int reason) { @@ -2005,7 +2003,7 @@ public abstract class NotificationListenerService extends Service { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; @@ -2081,7 +2079,7 @@ public abstract class NotificationListenerService extends Service { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; diff --git a/core/java/android/service/notification/NotificationRankingUpdate.java b/core/java/android/service/notification/NotificationRankingUpdate.java index 675c5cd63100..c64f4c46a769 100644 --- a/core/java/android/service/notification/NotificationRankingUpdate.java +++ b/core/java/android/service/notification/NotificationRankingUpdate.java @@ -15,6 +15,7 @@ */ package android.service.notification; +import android.annotation.Nullable; import android.os.Parcel; import android.os.Parcelable; @@ -42,7 +43,7 @@ public class NotificationRankingUpdate implements Parcelable { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; diff --git a/core/java/android/service/notification/NotificationStats.java b/core/java/android/service/notification/NotificationStats.java index 2cd8b8ba42d9..206e4fa4fb11 100644 --- a/core/java/android/service/notification/NotificationStats.java +++ b/core/java/android/service/notification/NotificationStats.java @@ -19,7 +19,6 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.app.RemoteInput; import android.os.Parcel; import android.os.Parcelable; @@ -31,7 +30,6 @@ import java.lang.annotation.RetentionPolicy; * Information about how the user has interacted with a given notification. * @hide */ -@TestApi @SystemApi public final class NotificationStats implements Parcelable { diff --git a/core/java/android/service/notification/NotifyingApp.java b/core/java/android/service/notification/NotifyingApp.java index a4fc5fde3bc2..930d14495c45 100644 --- a/core/java/android/service/notification/NotifyingApp.java +++ b/core/java/android/service/notification/NotifyingApp.java @@ -16,6 +16,7 @@ package android.service.notification; import android.annotation.NonNull; +import android.annotation.Nullable; import android.os.Parcel; import android.os.Parcelable; @@ -99,7 +100,7 @@ public final class NotifyingApp implements Parcelable, Comparable<NotifyingApp> } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; NotifyingApp that = (NotifyingApp) o; diff --git a/core/java/android/service/notification/SnoozeCriterion.java b/core/java/android/service/notification/SnoozeCriterion.java index eb624c9d85f4..d3da07a6fb80 100644 --- a/core/java/android/service/notification/SnoozeCriterion.java +++ b/core/java/android/service/notification/SnoozeCriterion.java @@ -17,7 +17,6 @@ package android.service.notification; import android.annotation.Nullable; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.os.Parcel; import android.os.Parcelable; @@ -27,7 +26,6 @@ import android.os.Parcelable; * @hide */ @SystemApi -@TestApi public final class SnoozeCriterion implements Parcelable { private final String mId; private final CharSequence mExplanation; diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java index 49bc65b3c8e5..a9ab33c0d1de 100644 --- a/core/java/android/service/notification/ZenModeConfig.java +++ b/core/java/android/service/notification/ZenModeConfig.java @@ -23,6 +23,7 @@ import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_FULL_SCRE import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_LIGHTS; import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK; +import android.annotation.Nullable; import android.app.ActivityManager; import android.app.AlarmManager; import android.app.NotificationManager; @@ -430,7 +431,7 @@ public class ZenModeConfig implements Parcelable { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (!(o instanceof ZenModeConfig)) return false; if (o == this) return true; final ZenModeConfig other = (ZenModeConfig) o; @@ -1527,7 +1528,7 @@ public class ZenModeConfig implements Parcelable { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (!(o instanceof ScheduleInfo)) return false; final ScheduleInfo other = (ScheduleInfo) o; return toDayList(days).equals(toDayList(other.days)) @@ -1629,7 +1630,7 @@ public class ZenModeConfig implements Parcelable { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (!(o instanceof EventInfo)) return false; final EventInfo other = (EventInfo) o; return userId == other.userId @@ -1934,7 +1935,7 @@ public class ZenModeConfig implements Parcelable { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (!(o instanceof ZenRule)) return false; if (o == this) return true; final ZenRule other = (ZenRule) o; diff --git a/core/java/android/service/notification/ZenPolicy.java b/core/java/android/service/notification/ZenPolicy.java index 6d0bcffe148e..ed3a9ac33738 100644 --- a/core/java/android/service/notification/ZenPolicy.java +++ b/core/java/android/service/notification/ZenPolicy.java @@ -18,6 +18,7 @@ package android.service.notification; import android.annotation.IntDef; import android.annotation.NonNull; +import android.annotation.Nullable; import android.app.Notification; import android.app.NotificationChannel; import android.os.Parcel; @@ -950,7 +951,7 @@ public final class ZenPolicy implements Parcelable { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (!(o instanceof ZenPolicy)) return false; if (o == this) return true; final ZenPolicy other = (ZenPolicy) o; diff --git a/core/java/android/service/textclassifier/TextClassifierService.java b/core/java/android/service/textclassifier/TextClassifierService.java index 93faa5872a69..1fb18fab3775 100644 --- a/core/java/android/service/textclassifier/TextClassifierService.java +++ b/core/java/android/service/textclassifier/TextClassifierService.java @@ -22,7 +22,6 @@ import android.annotation.MainThread; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.app.Service; import android.content.ComponentName; import android.content.Context; @@ -88,7 +87,6 @@ import java.util.concurrent.Executors; * @hide */ @SystemApi -@TestApi public abstract class TextClassifierService extends Service { private static final String LOG_TAG = "TextClassifierService"; diff --git a/core/java/android/service/voice/AlwaysOnHotwordDetector.java b/core/java/android/service/voice/AlwaysOnHotwordDetector.java index 8f8e6cc3d84a..b94031a5374e 100644 --- a/core/java/android/service/voice/AlwaysOnHotwordDetector.java +++ b/core/java/android/service/voice/AlwaysOnHotwordDetector.java @@ -379,16 +379,23 @@ public class AlwaysOnHotwordDetector { * Callbacks for always-on hotword detection. */ public static abstract class Callback { + /** - * Called when the hotword availability changes. - * This indicates a change in the availability of recognition for the given keyphrase. - * It's called at least once with the initial availability.<p/> + * Updates the availability state of the active keyphrase and locale on every keyphrase + * sound model change. + * + * <p>This API is called whenever there's a possibility that the keyphrase associated + * with this detector has been updated. It is not guaranteed that there is in fact any + * change, as it may be called for other reasons.</p> + * + * <p>This API is also guaranteed to be called right after an AlwaysOnHotwordDetector + * instance is created to updated the current availability state.</p> * - * Availability implies whether the hardware on this system is capable of listening for - * the given keyphrase or not. <p/> + * <p>Availability implies the current enrollment state of the given keyphrase. If the + * hardware on this system is not capable of listening for the given keyphrase, + * {@link AlwaysOnHotwordDetector#STATE_HARDWARE_UNAVAILABLE} will be returned. * * @see AlwaysOnHotwordDetector#STATE_HARDWARE_UNAVAILABLE - * @see AlwaysOnHotwordDetector#STATE_KEYPHRASE_UNSUPPORTED * @see AlwaysOnHotwordDetector#STATE_KEYPHRASE_UNENROLLED * @see AlwaysOnHotwordDetector#STATE_KEYPHRASE_ENROLLED */ diff --git a/core/java/android/service/voice/VoiceInteractionSession.java b/core/java/android/service/voice/VoiceInteractionSession.java index 0341b6d96b29..0b6d371d8235 100644 --- a/core/java/android/service/voice/VoiceInteractionSession.java +++ b/core/java/android/service/voice/VoiceInteractionSession.java @@ -2073,7 +2073,7 @@ public class VoiceInteractionSession implements KeyEvent.Callback, ComponentCall } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (this == o) { return true; } diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java index 0f46ffcb2d7f..6a70a856e09a 100644 --- a/core/java/android/service/wallpaper/WallpaperService.java +++ b/core/java/android/service/wallpaper/WallpaperService.java @@ -227,9 +227,6 @@ public abstract class WallpaperService extends Service { SurfaceControl mSurfaceControl = new SurfaceControl(); - // Unused relayout out-param - SurfaceControl mTmpSurfaceControl = new SurfaceControl(); - final BaseSurfaceHolder mSurfaceHolder = new BaseSurfaceHolder() { { mRequestedFormat = PixelFormat.RGBX_8888; @@ -880,9 +877,10 @@ public abstract class WallpaperService extends Service { InputChannel inputChannel = new InputChannel(); if (mSession.addToDisplay(mWindow, mLayout, View.VISIBLE, - mDisplay.getDisplayId(), mWinFrames.frame, mWinFrames.contentInsets, - mWinFrames.stableInsets, mWinFrames.displayCutout, inputChannel, - mInsetsState, mTempControls) < 0) { + mDisplay.getDisplayId(), mInsetsState, mWinFrames.frame, + mWinFrames.contentInsets, mWinFrames.stableInsets, + mWinFrames.displayCutout, inputChannel, mInsetsState, + mTempControls) < 0) { Log.w(TAG, "Failed to add window while updating wallpaper surface."); return; } @@ -905,7 +903,7 @@ public abstract class WallpaperService extends Service { final int relayoutResult = mSession.relayout( mWindow, mLayout, mWidth, mHeight, View.VISIBLE, 0, -1, mWinFrames, mMergedConfiguration, mSurfaceControl, - mInsetsState, mTempControls, mSurfaceSize, mTmpSurfaceControl); + mInsetsState, mTempControls, mSurfaceSize); if (mSurfaceControl.isValid()) { mSurfaceHolder.mSurface.copyFrom(mSurfaceControl); } diff --git a/core/java/android/service/watchdog/ExplicitHealthCheckService.java b/core/java/android/service/watchdog/ExplicitHealthCheckService.java index b1647fe0fcf9..49e00d6f6328 100644 --- a/core/java/android/service/watchdog/ExplicitHealthCheckService.java +++ b/core/java/android/service/watchdog/ExplicitHealthCheckService.java @@ -66,7 +66,6 @@ import java.util.concurrent.TimeUnit; * </pre> * @hide */ -@TestApi @SystemApi public abstract class ExplicitHealthCheckService extends Service { @@ -195,7 +194,6 @@ public abstract class ExplicitHealthCheckService extends Service { * * @hide */ - @TestApi @SystemApi public static final class PackageConfig implements Parcelable { private static final long DEFAULT_HEALTH_CHECK_TIMEOUT_MILLIS = TimeUnit.HOURS.toMillis(1); diff --git a/core/java/android/speech/tts/Voice.java b/core/java/android/speech/tts/Voice.java index fefc35e775e3..7ffe5eb7893d 100644 --- a/core/java/android/speech/tts/Voice.java +++ b/core/java/android/speech/tts/Voice.java @@ -16,6 +16,7 @@ package android.speech.tts; +import android.annotation.Nullable; import android.os.Parcel; import android.os.Parcelable; @@ -208,7 +209,7 @@ public class Voice implements Parcelable { } @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { if (this == obj) { return true; } diff --git a/core/java/android/telephony/DataConnectionRealTimeInfo.java b/core/java/android/telephony/DataConnectionRealTimeInfo.java index 8106f5f30d26..876713307e7d 100644 --- a/core/java/android/telephony/DataConnectionRealTimeInfo.java +++ b/core/java/android/telephony/DataConnectionRealTimeInfo.java @@ -16,6 +16,7 @@ package android.telephony; +import android.annotation.Nullable; import android.os.Parcel; import android.os.Parcelable; @@ -116,7 +117,7 @@ public class DataConnectionRealTimeInfo implements Parcelable { } @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { if (this == obj) { return true; } diff --git a/core/java/android/telephony/PhoneStateListener.java b/core/java/android/telephony/PhoneStateListener.java index 70c11f282a2b..6a5d5c63cb4d 100644 --- a/core/java/android/telephony/PhoneStateListener.java +++ b/core/java/android/telephony/PhoneStateListener.java @@ -20,7 +20,6 @@ import android.Manifest; import android.annotation.NonNull; import android.annotation.RequiresPermission; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.compat.annotation.ChangeId; import android.compat.annotation.UnsupportedAppUsage; import android.os.Binder; @@ -437,7 +436,6 @@ public class PhoneStateListener { * @hide */ @SystemApi - @TestApi @RequiresPermission(Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public static final int LISTEN_OUTGOING_EMERGENCY_CALL = 0x10000000; @@ -450,7 +448,6 @@ public class PhoneStateListener { * @hide */ @SystemApi - @TestApi @RequiresPermission(Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) public static final int LISTEN_OUTGOING_EMERGENCY_SMS = 0x20000000; @@ -969,7 +966,6 @@ public class PhoneStateListener { * @hide */ @SystemApi - @TestApi @Deprecated public void onOutgoingEmergencyCall(@NonNull EmergencyNumber placedEmergencyNumber) { // default implementation empty @@ -994,7 +990,6 @@ public class PhoneStateListener { * @hide */ @SystemApi - @TestApi public void onOutgoingEmergencyCall(@NonNull EmergencyNumber placedEmergencyNumber, int subscriptionId) { // Default implementation for backwards compatibility @@ -1011,7 +1006,6 @@ public class PhoneStateListener { * @hide */ @SystemApi - @TestApi @Deprecated public void onOutgoingEmergencySms(@NonNull EmergencyNumber sentEmergencyNumber) { // default implementation empty @@ -1033,7 +1027,6 @@ public class PhoneStateListener { * @hide */ @SystemApi - @TestApi public void onOutgoingEmergencySms(@NonNull EmergencyNumber sentEmergencyNumber, int subscriptionId) { // Default implementation for backwards compatibility diff --git a/core/java/android/text/SpannableStringBuilder.java b/core/java/android/text/SpannableStringBuilder.java index ac27e3d10c4f..c5f7f581dcd1 100644 --- a/core/java/android/text/SpannableStringBuilder.java +++ b/core/java/android/text/SpannableStringBuilder.java @@ -1607,7 +1607,7 @@ public class SpannableStringBuilder implements CharSequence, GetChars, Spannable // Same as SpannableStringInternal @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (o instanceof Spanned && toString().equals(o.toString())) { final Spanned other = (Spanned) o; diff --git a/core/java/android/text/SpannableStringInternal.java b/core/java/android/text/SpannableStringInternal.java index 4c9328a1c372..0fe9b6afeaea 100644 --- a/core/java/android/text/SpannableStringInternal.java +++ b/core/java/android/text/SpannableStringInternal.java @@ -16,6 +16,7 @@ package android.text; +import android.annotation.Nullable; import android.compat.annotation.UnsupportedAppUsage; import com.android.internal.util.ArrayUtils; @@ -501,7 +502,7 @@ import java.lang.reflect.Array; // Same as SpannableStringBuilder @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (o instanceof Spanned && toString().equals(o.toString())) { final Spanned other = (Spanned) o; diff --git a/core/java/android/text/StyledTextShaper.java b/core/java/android/text/StyledTextShaper.java deleted file mode 100644 index bf906143bc56..000000000000 --- a/core/java/android/text/StyledTextShaper.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.text; - -import android.annotation.NonNull; -import android.graphics.Paint; -import android.graphics.text.PositionedGlyphs; -import android.graphics.text.TextShaper; - -import java.util.List; - -/** - * Provides text shaping for multi-styled text. - * - * @see TextShaper#shapeTextRun(char[], int, int, int, int, float, float, boolean, Paint) - * @see TextShaper#shapeTextRun(CharSequence, int, int, int, int, float, float, boolean, Paint) - * @see StyledTextShaper#shapeText(CharSequence, int, int, TextDirectionHeuristic, TextPaint) - */ -public class StyledTextShaper { - private StyledTextShaper() {} - - - /** - * Shape multi-styled text. - * - * @param text a styled text. - * @param start a start index of shaping target in the text. - * @param count a length of shaping target in the text. - * @param dir a text direction. - * @param paint a paint - * @return a shape result. - */ - public static @NonNull List<PositionedGlyphs> shapeText( - @NonNull CharSequence text, int start, int count, - @NonNull TextDirectionHeuristic dir, @NonNull TextPaint paint) { - MeasuredParagraph mp = MeasuredParagraph.buildForBidi( - text, start, start + count, dir, null); - TextLine tl = TextLine.obtain(); - try { - tl.set(paint, text, start, start + count, - mp.getParagraphDir(), - mp.getDirections(start, start + count), - false /* tabstop is not supported */, - null, - -1, -1 // ellipsis is not supported. - ); - return tl.shape(); - } finally { - TextLine.recycle(tl); - } - } - -} diff --git a/core/java/android/text/TextLine.java b/core/java/android/text/TextLine.java index b82683260985..4471056e23dd 100644 --- a/core/java/android/text/TextLine.java +++ b/core/java/android/text/TextLine.java @@ -24,7 +24,7 @@ import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Paint.FontMetricsInt; import android.graphics.text.PositionedGlyphs; -import android.graphics.text.TextShaper; +import android.graphics.text.TextRunShaper; import android.os.Build; import android.text.Layout.Directions; import android.text.Layout.TabStops; @@ -37,7 +37,6 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.ArrayUtils; import java.util.ArrayList; -import java.util.List; /** * Represents a line of styled text, for measuring in visual order and @@ -312,8 +311,7 @@ public class TextLine { /** * Shape the TextLine. */ - List<PositionedGlyphs> shape() { - List<PositionedGlyphs> glyphs = new ArrayList<>(); + void shape(TextShaper.GlyphsConsumer consumer) { float horizontal = 0; float x = 0; final int runCount = mDirections.getRunCount(); @@ -326,7 +324,7 @@ public class TextLine { int segStart = runStart; for (int j = mHasTabs ? runStart : runLimit; j <= runLimit; j++) { if (j == runLimit || charAt(j) == TAB_CHAR) { - horizontal += shapeRun(glyphs, segStart, j, runIsRtl, x + horizontal, + horizontal += shapeRun(consumer, segStart, j, runIsRtl, x + horizontal, runIndex != (runCount - 1) || j != mLen); if (j != runLimit) { // charAt(j) == TAB_CHAR @@ -336,7 +334,6 @@ public class TextLine { } } } - return glyphs; } /** @@ -546,7 +543,7 @@ public class TextLine { /** * Shape a unidirectional (but possibly multi-styled) run of text. * - * @param glyphs the output positioned glyphs list + * @param consumer the consumer of the shape result * @param start the line-relative start * @param limit the line-relative limit * @param runIsRtl true if the run is right-to-left @@ -555,16 +552,17 @@ public class TextLine { * @return the signed width of the run, based on the paragraph direction. * Only valid if needWidth is true. */ - private float shapeRun(List<PositionedGlyphs> glyphs, int start, + private float shapeRun(TextShaper.GlyphsConsumer consumer, int start, int limit, boolean runIsRtl, float x, boolean needWidth) { if ((mDir == Layout.DIR_LEFT_TO_RIGHT) == runIsRtl) { float w = -measureRun(start, limit, limit, runIsRtl, null); - handleRun(start, limit, limit, runIsRtl, null, glyphs, x + w, 0, 0, 0, null, false); + handleRun(start, limit, limit, runIsRtl, null, consumer, x + w, 0, 0, 0, null, false); return w; } - return handleRun(start, limit, limit, runIsRtl, null, glyphs, x, 0, 0, 0, null, needWidth); + return handleRun(start, limit, limit, runIsRtl, null, consumer, x, 0, 0, 0, null, + needWidth); } @@ -899,7 +897,7 @@ public class TextLine { * @param end the end of the text * @param runIsRtl true if the run is right-to-left * @param c the canvas, can be null if rendering is not needed - * @param glyphs the output positioned glyph list, can be null if not necessary + * @param consumer the output positioned glyph list, can be null if not necessary * @param x the edge of the run closest to the leading margin * @param top the top of the line * @param y the baseline @@ -913,7 +911,7 @@ public class TextLine { */ private float handleText(TextPaint wp, int start, int end, int contextStart, int contextEnd, boolean runIsRtl, - Canvas c, List<PositionedGlyphs> glyphs, float x, int top, int y, int bottom, + Canvas c, TextShaper.GlyphsConsumer consumer, float x, int top, int y, int bottom, FontMetricsInt fmi, boolean needWidth, int offset, @Nullable ArrayList<DecorationInfo> decorations) { @@ -946,8 +944,8 @@ public class TextLine { rightX = x + totalWidth; } - if (glyphs != null) { - shapeTextRun(glyphs, wp, start, end, contextStart, contextEnd, runIsRtl, leftX); + if (consumer != null) { + shapeTextRun(consumer, wp, start, end, contextStart, contextEnd, runIsRtl, leftX); } if (c != null) { @@ -1135,7 +1133,7 @@ public class TextLine { * @param limit the limit of the run * @param runIsRtl true if the run is right-to-left * @param c the canvas, can be null - * @param glyphs the output positioned glyphs, can be null + * @param consumer the output positioned glyphs, can be null * @param x the end of the run closest to the leading margin * @param top the top of the line * @param y the baseline @@ -1147,7 +1145,7 @@ public class TextLine { */ private float handleRun(int start, int measureLimit, int limit, boolean runIsRtl, Canvas c, - List<PositionedGlyphs> glyphs, float x, int top, int y, + TextShaper.GlyphsConsumer consumer, float x, int top, int y, int bottom, FontMetricsInt fmi, boolean needWidth) { if (measureLimit < start || measureLimit > limit) { @@ -1180,7 +1178,7 @@ public class TextLine { wp.set(mPaint); wp.setStartHyphenEdit(adjustStartHyphenEdit(start, wp.getStartHyphenEdit())); wp.setEndHyphenEdit(adjustEndHyphenEdit(limit, wp.getEndHyphenEdit())); - return handleText(wp, start, limit, start, limit, runIsRtl, c, glyphs, x, top, + return handleText(wp, start, limit, start, limit, runIsRtl, c, consumer, x, top, y, bottom, fmi, needWidth, measureLimit, null); } @@ -1262,7 +1260,7 @@ public class TextLine { activePaint.setEndHyphenEdit( adjustEndHyphenEdit(activeEnd, mPaint.getEndHyphenEdit())); x += handleText(activePaint, activeStart, activeEnd, i, inext, runIsRtl, c, - glyphs, x, top, y, bottom, fmi, needWidth || activeEnd < measureLimit, + consumer, x, top, y, bottom, fmi, needWidth || activeEnd < measureLimit, Math.min(activeEnd, mlimit), mDecorations); activeStart = j; @@ -1288,7 +1286,7 @@ public class TextLine { adjustStartHyphenEdit(activeStart, mPaint.getStartHyphenEdit())); activePaint.setEndHyphenEdit( adjustEndHyphenEdit(activeEnd, mPaint.getEndHyphenEdit())); - x += handleText(activePaint, activeStart, activeEnd, i, inext, runIsRtl, c, glyphs, x, + x += handleText(activePaint, activeStart, activeEnd, i, inext, runIsRtl, c, consumer, x, top, y, bottom, fmi, needWidth || activeEnd < measureLimit, Math.min(activeEnd, mlimit), mDecorations); } @@ -1327,7 +1325,7 @@ public class TextLine { /** * Shape a text run with the set-up paint. * - * @param glyphs the output positioned glyphs list + * @param consumer the output positioned glyphs list * @param paint the paint used to render the text * @param start the start of the run * @param end the end of the run @@ -1336,30 +1334,32 @@ public class TextLine { * @param runIsRtl true if the run is right-to-left * @param x the x position of the left edge of the run */ - private void shapeTextRun(List<PositionedGlyphs> glyphs, TextPaint paint, + private void shapeTextRun(TextShaper.GlyphsConsumer consumer, TextPaint paint, int start, int end, int contextStart, int contextEnd, boolean runIsRtl, float x) { int count = end - start; int contextCount = contextEnd - contextStart; + PositionedGlyphs glyphs; if (mCharsValid) { - glyphs.add(TextShaper.shapeTextRun( + glyphs = TextRunShaper.shapeTextRun( mChars, start, count, contextStart, contextCount, x, 0f, runIsRtl, paint - )); + ); } else { - glyphs.add(TextShaper.shapeTextRun( + glyphs = TextRunShaper.shapeTextRun( mText, mStart + start, count, mStart + contextStart, contextCount, x, 0f, runIsRtl, paint - )); + ); } + consumer.accept(start, count, glyphs, paint); } diff --git a/core/java/android/text/TextShaper.java b/core/java/android/text/TextShaper.java new file mode 100644 index 000000000000..dd2570401b3e --- /dev/null +++ b/core/java/android/text/TextShaper.java @@ -0,0 +1,229 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.text; + +import android.annotation.IntRange; +import android.annotation.NonNull; +import android.graphics.Paint; +import android.graphics.text.PositionedGlyphs; +import android.graphics.text.TextRunShaper; + +/** + * Provides text shaping for multi-styled text. + * + * Here is an example of animating text size and letter spacing for simple text. + * <pre> + * <code> + * // In this example, shape the text once for start and end state, then animate between two shape + * // result without re-shaping in each frame. + * class SimpleAnimationView @JvmOverloads constructor( + * context: Context, + * attrs: AttributeSet? = null, + * defStyleAttr: Int = 0 + * ) : View(context, attrs, defStyleAttr) { + * private val textDir = TextDirectionHeuristics.LOCALE + * private val text = "Hello, World." // The text to be displayed + * + * // Class for keeping drawing parameters. + * data class DrawStyle(val textSize: Float, val alpha: Int) + * + * // The start and end text shaping result. This class will animate between these two. + * private val start = mutableListOf<Pair<PositionedGlyphs, DrawStyle>>() + * private val end = mutableListOf<Pair<PositionedGlyphs, DrawStyle>>() + * + * init { + * val startPaint = TextPaint().apply { + * alpha = 0 // Alpha only affect text drawing but not text shaping + * textSize = 36f // TextSize affect both text shaping and drawing. + * letterSpacing = 0f // Letter spacing only affect text shaping but not drawing. + * } + * + * val endPaint = TextPaint().apply { + * alpha = 255 + * textSize =128f + * letterSpacing = 0.1f + * } + * + * TextShaper.shapeText(text, 0, text.length, textDir, startPaint) { _, _, glyphs, paint -> + * start.add(Pair(glyphs, DrawStyle(paint.textSize, paint.alpha))) + * } + * TextShaper.shapeText(text, 0, text.length, textDir, endPaint) { _, _, glyphs, paint -> + * end.add(Pair(glyphs, DrawStyle(paint.textSize, paint.alpha))) + * } + * } + * + * override fun onDraw(canvas: Canvas) { + * super.onDraw(canvas) + * + * // Set the baseline to the vertical center of the view. + * canvas.translate(0f, height / 2f) + * + * // Assume the number of PositionedGlyphs are the same. If different, you may want to + * // animate in a different way, e.g. cross fading. + * start.zip(end) { (startGlyphs, startDrawStyle), (endGlyphs, endDrawStyle) -> + * // Tween the style and set to paint. + * paint.textSize = lerp(startDrawStyle.textSize, endDrawStyle.textSize, progress) + * paint.alpha = lerp(startDrawStyle.alpha, endDrawStyle.alpha, progress) + * + * // Assume the number of glyphs are the same. If different, you may want to animate in + * // a different way, e.g. cross fading. + * require(startGlyphs.glyphCount() == endGlyphs.glyphCount()) + * + * if (startGlyphs.glyphCount() == 0) return@zip + * + * var curFont = startGlyphs.getFont(0) + * var drawStart = 0 + * for (i in 1 until startGlyphs.glyphCount()) { + * // Assume the pair of Glyph ID and font is the same. If different, you may want + * // to animate in a different way, e.g. cross fading. + * require(startGlyphs.getGlyphId(i) == endGlyphs.getGlyphId(i)) + * require(startGlyphs.getFont(i) === endGlyphs.getFont(i)) + * + * val font = startGlyphs.getFont(i) + * if (curFont != font) { + * drawGlyphs(canvas, startGlyphs, endGlyphs, drawStart, i, curFont, paint) + * curFont = font + * drawStart = i + * } + * } + * if (drawStart != startGlyphs.glyphCount() - 1) { + * drawGlyphs(canvas, startGlyphs, endGlyphs, drawStart, startGlyphs.glyphCount(), + * curFont, paint) + * } + * } + * } + * + * // Draws Glyphs for the same font run. + * private fun drawGlyphs(canvas: Canvas, startGlyph: PositionedGlyphs, + * endGlyph: PositionedGlyphs, start: Int, end: Int, font: Font, + * paint: Paint) { + * var cacheIndex = 0 + * for (i in start until end) { + * intArrayCache[cacheIndex] = startGlyph.getGlyphId(i) + * // The glyph positions are different from start to end since they are shaped + * // with different letter spacing. Use linear interpolation for positions + * // during animation. + * floatArrayCache[cacheIndex * 2] = + * lerp(startGlyph.getGlyphX(i), endGlyph.getGlyphX(i), progress) + * floatArrayCache[cacheIndex * 2 + 1] = + * lerp(startGlyph.getGlyphY(i), endGlyph.getGlyphY(i), progress) + * if (cacheIndex == CACHE_SIZE) { // Cached int array is full. Flashing. + * canvas.drawGlyphs( + * intArrayCache, 0, // glyphID array and its starting offset + * floatArrayCache, 0, // position array and its starting offset + * cacheIndex, // glyph count + * font, + * paint + * ) + * cacheIndex = 0 + * } + * cacheIndex++ + * } + * if (cacheIndex != 0) { + * canvas.drawGlyphs( + * intArrayCache, 0, // glyphID array and its starting offset + * floatArrayCache, 0, // position array and its starting offset + * cacheIndex, // glyph count + * font, + * paint + * ) + * } + * } + * + * // Linear Interpolator + * private fun lerp(start: Float, end: Float, t: Float) = start * (1f - t) + end * t + * private fun lerp(start: Int, end: Int, t: Float) = (start * (1f - t) + end * t).toInt() + * + * // The animation progress. + * var progress: Float = 0f + * set(value) { + * field = value + * invalidate() + * } + * + * // working copy of paint. + * private val paint = Paint() + * + * // Array cache for reducing allocation during drawing. + * private var intArrayCache = IntArray(CACHE_SIZE) + * private var floatArrayCache = FloatArray(CACHE_SIZE * 2) + * } + * </code> + * </pre> + * @see TextRunShaper#shapeTextRun(char[], int, int, int, int, float, float, boolean, Paint) + * @see TextRunShaper#shapeTextRun(CharSequence, int, int, int, int, float, float, boolean, Paint) + * @see TextShaper#shapeText(CharSequence, int, int, TextDirectionHeuristic, TextPaint, + * GlyphsConsumer) + */ +public class TextShaper { + private TextShaper() {} + + /** + * An consumer interface for accepting text shape result. + */ + public interface GlyphsConsumer { + /** + * Accept text shape result. + * + * The implementation must not keep reference of paint since it will be mutated for the + * subsequent styles. Also, for saving heap size, keep only necessary members in the + * {@link TextPaint} instead of copying {@link TextPaint} object. + * + * @param start The start index of the shaped text. + * @param count The length of the shaped text. + * @param glyphs The shape result. + * @param paint The paint to be used for drawing. + */ + void accept( + @IntRange(from = 0) int start, + @IntRange(from = 0) int count, + @NonNull PositionedGlyphs glyphs, + @NonNull TextPaint paint); + } + + /** + * Shape multi-styled text. + * + * @param text a styled text. + * @param start a start index of shaping target in the text. + * @param count a length of shaping target in the text. + * @param dir a text direction. + * @param paint a paint + * @param consumer a consumer of the shape result. + */ + public static void shapeText( + @NonNull CharSequence text, @IntRange(from = 0) int start, + @IntRange(from = 0) int count, @NonNull TextDirectionHeuristic dir, + @NonNull TextPaint paint, @NonNull GlyphsConsumer consumer) { + MeasuredParagraph mp = MeasuredParagraph.buildForBidi( + text, start, start + count, dir, null); + TextLine tl = TextLine.obtain(); + try { + tl.set(paint, text, start, start + count, + mp.getParagraphDir(), + mp.getDirections(start, start + count), + false /* tabstop is not supported */, + null, + -1, -1 // ellipsis is not supported. + ); + tl.shape(consumer); + } finally { + TextLine.recycle(tl); + } + } + +} diff --git a/core/java/android/text/TextUtils.java b/core/java/android/text/TextUtils.java index d441e6bbf8cb..72b35b9253eb 100644 --- a/core/java/android/text/TextUtils.java +++ b/core/java/android/text/TextUtils.java @@ -2080,6 +2080,94 @@ public class TextUtils { } /** + * Simple alternative to {@link String#format} which purposefully supports + * only a small handful of substitutions to improve execution speed. + * Benchmarking reveals this optimized alternative performs 6.5x faster for + * a typical format string. + * <p> + * Below is a summary of the limited grammar supported by this method; if + * you need advanced features, please continue using {@link String#format}. + * <ul> + * <li>{@code %b} for {@code boolean} + * <li>{@code %c} for {@code char} + * <li>{@code %d} for {@code int} or {@code long} + * <li>{@code %f} for {@code float} or {@code double} + * <li>{@code %s} for {@code String} + * <li>{@code %x} for hex representation of {@code int} or {@code long} + * <li>{@code %%} for literal {@code %} + * </ul> + * + * @throws IllegalArgumentException if the format string or arguments don't + * match the supported grammar described above. + * @hide + */ + public static @NonNull String formatSimple(@NonNull String format, Object... args) { + final StringBuilder sb = new StringBuilder(format); + int j = 0; + for (int i = 0; i < sb.length(); ) { + if (sb.charAt(i) == '%') { + final String repl; + final char code = sb.charAt(i + 1); + switch (code) { + case 'b': { + if (j == args.length) { + throw new IllegalArgumentException("Too few arguments"); + } + final Object arg = args[j++]; + if (arg instanceof Boolean) { + repl = Boolean.toString((boolean) arg); + } else { + repl = Boolean.toString(arg != null); + } + break; + } + case 'c': + case 'd': + case 'f': + case 's': { + if (j == args.length) { + throw new IllegalArgumentException("Too few arguments"); + } + final Object arg = args[j++]; + repl = String.valueOf(arg); + break; + } + case 'x': { + if (j == args.length) { + throw new IllegalArgumentException("Too few arguments"); + } + final Object arg = args[j++]; + if (arg instanceof Integer) { + repl = Integer.toHexString((int) arg); + } else if (arg instanceof Long) { + repl = Long.toHexString((long) arg); + } else { + throw new IllegalArgumentException( + "Unsupported hex type " + arg.getClass()); + } + break; + } + case '%': { + repl = "%"; + break; + } + default: { + throw new IllegalArgumentException("Unsupported format code " + code); + } + } + sb.replace(i, i + 2, repl); + i += repl.length(); + } else { + i++; + } + } + if (j != args.length) { + throw new IllegalArgumentException("Too many arguments"); + } + return sb.toString(); + } + + /** * Returns whether or not the specified spanned text has a style span. * @hide */ diff --git a/core/java/android/text/format/DateFormat.java b/core/java/android/text/format/DateFormat.java index 38e3b39f8cfc..4a0bec1300b7 100755 --- a/core/java/android/text/format/DateFormat.java +++ b/core/java/android/text/format/DateFormat.java @@ -17,10 +17,14 @@ package android.text.format; import android.annotation.NonNull; +import android.app.compat.CompatChanges; +import android.compat.annotation.ChangeId; +import android.compat.annotation.EnabledAfter; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.icu.text.DateFormatSymbols; import android.icu.text.DateTimePatternGenerator; +import android.os.Build; import android.provider.Settings; import android.text.SpannableStringBuilder; import android.text.Spanned; @@ -159,6 +163,16 @@ public class DateFormat { private static boolean sIs24Hour; /** + * {@link #getBestDateTimePattern(Locale, String)} does not allow non-consecutive repeated + * symbol in the skeleton. For example, please use a skeleton of {@code "jmm"} or + * {@code "hmma"} instead of {@code "ahmma"} or {@code "jmma"}, because the field 'j' could + * mean using 12-hour in some locales and, in this case, is duplicated as the 'a' field. + */ + @ChangeId + @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.R) + static final long DISALLOW_DUPLICATE_FIELD_IN_SKELETON = 170233598L; + + /** * Returns true if times should be formatted as 24 hour times, false if times should be * formatted as 12 hour (AM/PM) times. Based on the user's chosen locale and other preferences. * @param context the context to use for the content resolver @@ -251,7 +265,9 @@ public class DateFormat { */ public static String getBestDateTimePattern(Locale locale, String skeleton) { DateTimePatternGenerator dtpg = DateTimePatternGenerator.getInstance(locale); - return dtpg.getBestPattern(skeleton); + boolean allowDuplicateFields = !CompatChanges.isChangeEnabled( + DISALLOW_DUPLICATE_FIELD_IN_SKELETON); + return dtpg.getBestPattern(skeleton, allowDuplicateFields); } /** diff --git a/core/java/android/text/style/SuggestionSpan.java b/core/java/android/text/style/SuggestionSpan.java index ac8005b39469..9378636c24ca 100644 --- a/core/java/android/text/style/SuggestionSpan.java +++ b/core/java/android/text/style/SuggestionSpan.java @@ -349,7 +349,7 @@ public class SuggestionSpan extends CharacterStyle implements ParcelableSpan { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (o instanceof SuggestionSpan) { return ((SuggestionSpan)o).hashCode() == mHashCode; } diff --git a/core/java/android/text/util/Rfc822Token.java b/core/java/android/text/util/Rfc822Token.java index 058757a03098..2f207db9d494 100644 --- a/core/java/android/text/util/Rfc822Token.java +++ b/core/java/android/text/util/Rfc822Token.java @@ -191,7 +191,7 @@ public class Rfc822Token { } } - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (!(o instanceof Rfc822Token)) { return false; } diff --git a/core/java/android/timezone/CountryTimeZones.java b/core/java/android/timezone/CountryTimeZones.java index 44d140242409..d0bcfda062f1 100644 --- a/core/java/android/timezone/CountryTimeZones.java +++ b/core/java/android/timezone/CountryTimeZones.java @@ -66,7 +66,7 @@ public final class CountryTimeZones { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (this == o) { return true; } @@ -120,7 +120,7 @@ public final class CountryTimeZones { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (this == o) { return true; } @@ -272,7 +272,7 @@ public final class CountryTimeZones { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (this == o) { return true; } diff --git a/core/java/android/timezone/TelephonyNetwork.java b/core/java/android/timezone/TelephonyNetwork.java index 3b65c6ffc379..161ecb4e57c9 100644 --- a/core/java/android/timezone/TelephonyNetwork.java +++ b/core/java/android/timezone/TelephonyNetwork.java @@ -17,6 +17,7 @@ package android.timezone; import android.annotation.NonNull; +import android.annotation.Nullable; import java.util.Objects; @@ -59,7 +60,7 @@ public final class TelephonyNetwork { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (this == o) { return true; } diff --git a/core/java/android/timezone/TzDataSetVersion.java b/core/java/android/timezone/TzDataSetVersion.java index e1fb932b977d..edcbbb354089 100644 --- a/core/java/android/timezone/TzDataSetVersion.java +++ b/core/java/android/timezone/TzDataSetVersion.java @@ -17,6 +17,7 @@ package android.timezone; import android.annotation.NonNull; +import android.annotation.Nullable; import com.android.internal.annotations.VisibleForTesting; @@ -128,7 +129,7 @@ public final class TzDataSetVersion { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (this == o) { return true; } diff --git a/core/java/android/transition/ChangeTransform.java b/core/java/android/transition/ChangeTransform.java index 5303855c7003..02d0a6a1dba9 100644 --- a/core/java/android/transition/ChangeTransform.java +++ b/core/java/android/transition/ChangeTransform.java @@ -20,6 +20,7 @@ import android.animation.AnimatorListenerAdapter; import android.animation.FloatArrayEvaluator; import android.animation.ObjectAnimator; import android.animation.PropertyValuesHolder; +import android.annotation.Nullable; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Matrix; @@ -450,7 +451,7 @@ public class ChangeTransform extends Transition { } @Override - public boolean equals(Object that) { + public boolean equals(@Nullable Object that) { if (!(that instanceof Transforms)) { return false; } diff --git a/core/java/android/transition/TransitionValues.java b/core/java/android/transition/TransitionValues.java index 85fa67df6d74..bb68b4c3473d 100644 --- a/core/java/android/transition/TransitionValues.java +++ b/core/java/android/transition/TransitionValues.java @@ -17,6 +17,7 @@ package android.transition; import android.annotation.NonNull; +import android.annotation.Nullable; import android.util.ArrayMap; import android.view.View; import android.view.ViewGroup; @@ -73,7 +74,7 @@ public class TransitionValues { final ArrayList<Transition> targetedTransitions = new ArrayList<Transition>(); @Override - public boolean equals(Object other) { + public boolean equals(@Nullable Object other) { if (other instanceof TransitionValues) { if (view == ((TransitionValues) other).view) { if (values.equals(((TransitionValues) other).values)) { diff --git a/core/java/android/util/ArrayMap.java b/core/java/android/util/ArrayMap.java index 4cf0a36d24bb..4edff27d0ced 100644 --- a/core/java/android/util/ArrayMap.java +++ b/core/java/android/util/ArrayMap.java @@ -16,6 +16,7 @@ package android.util; +import android.annotation.Nullable; import android.compat.annotation.UnsupportedAppUsage; import com.android.internal.util.ArrayUtils; @@ -826,7 +827,7 @@ public final class ArrayMap<K, V> implements Map<K, V> { * equal, the method returns false, otherwise it returns true. */ @Override - public boolean equals(Object object) { + public boolean equals(@Nullable Object object) { if (this == object) { return true; } diff --git a/core/java/android/util/ArraySet.java b/core/java/android/util/ArraySet.java index 7f652ba2cd3a..f53548a41177 100644 --- a/core/java/android/util/ArraySet.java +++ b/core/java/android/util/ArraySet.java @@ -778,7 +778,7 @@ public final class ArraySet<E> implements Collection<E>, Set<E> { * returns true. */ @Override - public boolean equals(Object object) { + public boolean equals(@Nullable Object object) { if (this == object) { return true; } diff --git a/core/java/android/util/DisplayMetrics.java b/core/java/android/util/DisplayMetrics.java index 9f6065e695c5..0a3e6b1cff38 100755 --- a/core/java/android/util/DisplayMetrics.java +++ b/core/java/android/util/DisplayMetrics.java @@ -16,10 +16,10 @@ package android.util; +import android.annotation.Nullable; import android.compat.annotation.UnsupportedAppUsage; import android.os.SystemProperties; - /** * A structure describing general information about a display, such as its * size, density, and font scaling. @@ -362,7 +362,7 @@ public class DisplayMetrics { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { return o instanceof DisplayMetrics && equals((DisplayMetrics)o); } diff --git a/core/java/android/util/EventLog.java b/core/java/android/util/EventLog.java index ead4e46cd28b..ee98b65d2444 100644 --- a/core/java/android/util/EventLog.java +++ b/core/java/android/util/EventLog.java @@ -307,7 +307,7 @@ public class EventLog { * @hide */ @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { // Not using ByteBuffer.equals since it takes buffer position into account and we // always use absolute positions here. if (this == o) return true; diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java index 9244647432c7..c20b0639b1c4 100644 --- a/core/java/android/util/FeatureFlagUtils.java +++ b/core/java/android/util/FeatureFlagUtils.java @@ -36,7 +36,6 @@ public class FeatureFlagUtils { public static final String FFLAG_PREFIX = "sys.fflag."; public static final String FFLAG_OVERRIDE_PREFIX = FFLAG_PREFIX + "override."; public static final String PERSIST_PREFIX = "persist." + FFLAG_OVERRIDE_PREFIX; - public static final String SEAMLESS_TRANSFER = "settings_seamless_transfer"; public static final String HEARING_AID_SETTINGS = "settings_bluetooth_hearing_aid"; public static final String SCREENRECORD_LONG_PRESS = "settings_screenrecord_long_press"; public static final String DYNAMIC_SYSTEM = "settings_dynamic_system"; @@ -54,7 +53,6 @@ public class FeatureFlagUtils { DEFAULT_FLAGS.put("settings_audio_switcher", "true"); DEFAULT_FLAGS.put("settings_systemui_theme", "true"); DEFAULT_FLAGS.put(DYNAMIC_SYSTEM, "false"); - DEFAULT_FLAGS.put(SEAMLESS_TRANSFER, "false"); DEFAULT_FLAGS.put(HEARING_AID_SETTINGS, "false"); DEFAULT_FLAGS.put(SCREENRECORD_LONG_PRESS, "false"); DEFAULT_FLAGS.put("settings_wifi_details_datausage_header", "false"); diff --git a/core/java/android/util/Log.java b/core/java/android/util/Log.java index 7a117f14d9b5..12bcd8b0aa97 100644 --- a/core/java/android/util/Log.java +++ b/core/java/android/util/Log.java @@ -44,9 +44,7 @@ import java.net.UnknownHostException; * You can then <a href="{@docRoot}studio/debug/am-logcat.html">view the logs in logcat</a>. * * <p>The order in terms of verbosity, from least to most is - * ERROR, WARN, INFO, DEBUG, VERBOSE. Verbose should never be compiled - * into an application except during development. Debug logs are compiled - * in but stripped at runtime. Error, warning and info logs are always kept. + * ERROR, WARN, INFO, DEBUG, VERBOSE. * * <p><b>Tip:</b> A good convention is to declare a <code>TAG</code> constant * in your class: diff --git a/core/java/android/util/MapCollections.java b/core/java/android/util/MapCollections.java index a5212688c6c6..7ab3fcaeedc4 100644 --- a/core/java/android/util/MapCollections.java +++ b/core/java/android/util/MapCollections.java @@ -16,6 +16,8 @@ package android.util; +import android.annotation.Nullable; + import java.lang.reflect.Array; import java.util.Collection; import java.util.Iterator; @@ -249,7 +251,7 @@ abstract class MapCollections<K, V> { } @Override - public boolean equals(Object object) { + public boolean equals(@Nullable Object object) { return equalsSetHelper(this, object); } @@ -339,7 +341,7 @@ abstract class MapCollections<K, V> { } @Override - public boolean equals(Object object) { + public boolean equals(@Nullable Object object) { return equalsSetHelper(this, object); } diff --git a/core/java/android/util/MemoryIntArray.java b/core/java/android/util/MemoryIntArray.java index 7d287e38ba86..9073d3ae91ca 100644 --- a/core/java/android/util/MemoryIntArray.java +++ b/core/java/android/util/MemoryIntArray.java @@ -16,13 +16,15 @@ package android.util; +import android.annotation.Nullable; import android.os.Parcel; import android.os.ParcelFileDescriptor; import android.os.Parcelable; -import libcore.io.IoUtils; import dalvik.system.CloseGuard; +import libcore.io.IoUtils; + import java.io.Closeable; import java.io.IOException; import java.util.UUID; @@ -183,7 +185,7 @@ public final class MemoryIntArray implements Parcelable, Closeable { } @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { if (obj == null) { return false; } diff --git a/core/java/android/util/MergedConfiguration.java b/core/java/android/util/MergedConfiguration.java index 2399adac3cba..3ac44aacc2c7 100644 --- a/core/java/android/util/MergedConfiguration.java +++ b/core/java/android/util/MergedConfiguration.java @@ -17,6 +17,7 @@ package android.util; import android.annotation.NonNull; +import android.annotation.Nullable; import android.content.res.Configuration; import android.os.Parcel; import android.os.Parcelable; @@ -167,7 +168,7 @@ public class MergedConfiguration implements Parcelable { } @Override - public boolean equals(Object that) { + public boolean equals(@Nullable Object that) { if (!(that instanceof MergedConfiguration)) { return false; } diff --git a/core/java/android/util/Pair.java b/core/java/android/util/Pair.java index f96da729b831..b0866b440be5 100644 --- a/core/java/android/util/Pair.java +++ b/core/java/android/util/Pair.java @@ -16,6 +16,8 @@ package android.util; +import android.annotation.Nullable; + import java.util.Objects; /** @@ -47,7 +49,7 @@ public class Pair<F, S> { * equal */ @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (!(o instanceof Pair)) { return false; } diff --git a/core/java/android/util/Range.java b/core/java/android/util/Range.java index f31ddd9b0307..9fd0ab99f01b 100644 --- a/core/java/android/util/Range.java +++ b/core/java/android/util/Range.java @@ -18,6 +18,7 @@ package android.util; import static com.android.internal.util.Preconditions.*; +import android.annotation.Nullable; import android.hardware.camera2.utils.HashCodeHelpers; /** @@ -146,7 +147,7 @@ public final class Range<T extends Comparable<? super T>> { * @return {@code true} if the ranges are equal, {@code false} otherwise */ @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { if (obj == null) { return false; } else if (this == obj) { diff --git a/core/java/android/util/Rational.java b/core/java/android/util/Rational.java index 593000002cf7..aade6204aff8 100644 --- a/core/java/android/util/Rational.java +++ b/core/java/android/util/Rational.java @@ -17,6 +17,7 @@ package android.util; import static com.android.internal.util.Preconditions.checkNotNull; +import android.annotation.Nullable; import android.compat.annotation.UnsupportedAppUsage; import java.io.IOException; @@ -240,7 +241,7 @@ public final class Rational extends Number implements Comparable<Rational> { * @return A boolean that determines whether or not the two Rational objects are equal. */ @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { return obj instanceof Rational && equals((Rational) obj); } diff --git a/core/java/android/util/RecurrenceRule.java b/core/java/android/util/RecurrenceRule.java index a570e5e00b1c..0f2d8bc9e375 100644 --- a/core/java/android/util/RecurrenceRule.java +++ b/core/java/android/util/RecurrenceRule.java @@ -16,6 +16,7 @@ package android.util; +import android.annotation.Nullable; import android.compat.annotation.UnsupportedAppUsage; import android.os.Parcel; import android.os.Parcelable; @@ -130,7 +131,7 @@ public class RecurrenceRule implements Parcelable { } @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { if (obj instanceof RecurrenceRule) { final RecurrenceRule other = (RecurrenceRule) obj; return Objects.equals(start, other.start) diff --git a/core/java/android/util/SparseBooleanArray.java b/core/java/android/util/SparseBooleanArray.java index 846df397aa6d..c145b20e6f6c 100644 --- a/core/java/android/util/SparseBooleanArray.java +++ b/core/java/android/util/SparseBooleanArray.java @@ -16,6 +16,7 @@ package android.util; +import android.annotation.Nullable; import android.compat.annotation.UnsupportedAppUsage; import com.android.internal.util.ArrayUtils; @@ -289,7 +290,7 @@ public class SparseBooleanArray implements Cloneable { } @Override - public boolean equals(Object that) { + public boolean equals(@Nullable Object that) { if (this == that) { return true; } diff --git a/core/java/android/util/apk/VerbatimX509Certificate.java b/core/java/android/util/apk/VerbatimX509Certificate.java index 391c5fc39416..d9a00b232484 100644 --- a/core/java/android/util/apk/VerbatimX509Certificate.java +++ b/core/java/android/util/apk/VerbatimX509Certificate.java @@ -16,6 +16,8 @@ package android.util.apk; +import android.annotation.Nullable; + import java.security.cert.CertificateEncodingException; import java.security.cert.X509Certificate; import java.util.Arrays; @@ -39,7 +41,7 @@ class VerbatimX509Certificate extends WrappedX509Certificate { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (this == o) return true; if (!(o instanceof VerbatimX509Certificate)) return false; diff --git a/core/java/android/util/jar/StrictJarManifest.java b/core/java/android/util/jar/StrictJarManifest.java index faec099b001f..5c2fd9e41b9b 100644 --- a/core/java/android/util/jar/StrictJarManifest.java +++ b/core/java/android/util/jar/StrictJarManifest.java @@ -17,6 +17,10 @@ package android.util.jar; +import android.annotation.Nullable; + +import libcore.io.Streams; + import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -29,7 +33,6 @@ import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.jar.Attributes; -import libcore.io.Streams; /** * The {@code StrictJarManifest} class is used to obtain attribute information for a @@ -219,7 +222,7 @@ public class StrictJarManifest implements Cloneable { * @return {@code true} if the manifests are equal, {@code false} otherwise */ @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (o == null) { return false; } diff --git a/core/java/android/uwb/AngleMeasurement.java b/core/java/android/uwb/AngleMeasurement.java new file mode 100644 index 000000000000..c3e2ecc9e0f3 --- /dev/null +++ b/core/java/android/uwb/AngleMeasurement.java @@ -0,0 +1,149 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.uwb; + +import android.annotation.FloatRange; + +/** + * Angle measurement + * + * <p>The actual angle is interpreted as: + * {@link #getRadians()} +/- {@link #getErrorRadians()} ()} at {@link #getConfidenceLevel()} + * + * @hide + */ +public final class AngleMeasurement { + private final double mRadians; + private final double mErrorRadians; + private final double mConfidenceLevel; + + private AngleMeasurement(double radians, double errorRadians, double confidenceLevel) { + mRadians = radians; + mErrorRadians = errorRadians; + mConfidenceLevel = confidenceLevel; + } + + /** + * Angle measurement in radians + * + * @return angle in radians + */ + @FloatRange(from = -Math.PI, to = +Math.PI) + public double getRadians() { + return mRadians; + } + + /** + * Error of angle measurement in radians + * + * <p>Must be a positive value + * + * @return angle measurement error in radians + */ + @FloatRange(from = 0.0, to = +Math.PI) + public double getErrorRadians() { + return mErrorRadians; + } + + /** + * Angle measurement confidence level expressed as a value between + * 0.0 to 1.0. + * + * <p>A value of 0.0 indicates there is no confidence in the measurement. A value of 1.0 + * indicates there is maximum confidence in the measurement. + * + * @return the confidence level of the angle measurement + */ + @FloatRange(from = 0.0, to = 1.0) + public double getConfidenceLevel() { + return mConfidenceLevel; + } + + /** + * Builder class for {@link AngleMeasurement}. + */ + public static final class Builder { + private double mRadians = Double.NaN; + private double mErrorRadians = Double.NaN; + private double mConfidenceLevel = Double.NaN; + + /** + * Set the angle in radians + * + * @param radians angle in radians + * @throws IllegalArgumentException if angle exceeds allowed limits of [-Math.PI, +Math.PI] + */ + public Builder setRadians(double radians) { + if (radians < -Math.PI || radians > Math.PI) { + throw new IllegalArgumentException("Invalid radians: " + radians); + } + mRadians = radians; + return this; + } + + /** + * Set the angle error in radians + * + * @param errorRadians error of the angle in radians + * @throws IllegalArgumentException if the error exceeds the allowed limits of [0, +Math.PI] + */ + public Builder setErrorRadians(double errorRadians) { + if (errorRadians < 0.0 || errorRadians > Math.PI) { + throw new IllegalArgumentException( + "Invalid error radians: " + errorRadians); + } + mErrorRadians = errorRadians; + return this; + } + + /** + * Set the angle confidence level + * + * @param confidenceLevel level of confidence of the angle measurement + * @throws IllegalArgumentException if the error exceeds the allowed limits of [0.0, 1.0] + */ + public Builder setConfidenceLevel(double confidenceLevel) { + if (confidenceLevel < 0.0 || confidenceLevel > 1.0) { + throw new IllegalArgumentException( + "Invalid confidence level: " + confidenceLevel); + } + mConfidenceLevel = confidenceLevel; + return this; + } + + /** + * Build the {@link AngleMeasurement} object + * + * @throws IllegalStateException if angle, error, or confidence values are missing + */ + public AngleMeasurement build() { + if (Double.isNaN(mRadians)) { + throw new IllegalStateException("Angle is not set"); + } + + if (Double.isNaN(mErrorRadians)) { + throw new IllegalStateException("Angle error is not set"); + } + + if (Double.isNaN(mConfidenceLevel)) { + throw new IllegalStateException("Angle confidence level is not set"); + } + + return new AngleMeasurement(mRadians, mErrorRadians, mConfidenceLevel); + } + } +} diff --git a/core/java/android/uwb/AngleOfArrivalMeasurement.java b/core/java/android/uwb/AngleOfArrivalMeasurement.java new file mode 100644 index 000000000000..a7b5eae728cb --- /dev/null +++ b/core/java/android/uwb/AngleOfArrivalMeasurement.java @@ -0,0 +1,114 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.uwb; + +import android.annotation.NonNull; +import android.annotation.Nullable; + +/** + * Represents an angle of arrival measurement between two devices using Ultra Wideband + * + * @hide + */ +public final class AngleOfArrivalMeasurement { + private final AngleMeasurement mAzimuthAngleMeasurement; + private final AngleMeasurement mAltitudeAngleMeasurement; + + private AngleOfArrivalMeasurement(@NonNull AngleMeasurement azimuthAngleMeasurement, + @Nullable AngleMeasurement altitudeAngleMeasurement) { + mAzimuthAngleMeasurement = azimuthAngleMeasurement; + mAltitudeAngleMeasurement = altitudeAngleMeasurement; + } + + /** + * Azimuth angle measurement + * <p>Azimuth {@link AngleMeasurement} of remote device in horizontal coordinate system, this is + * the angle clockwise from the meridian when viewing above the north pole. + * + * <p>See: https://en.wikipedia.org/wiki/Horizontal_coordinate_system + * + * <p>On an Android device, azimuth north is defined as the angle perpendicular away from the + * back of the device when holding it in portrait mode upright. + * + * <p>Azimuth angle must be supported when Angle of Arrival is supported + * + * @return the azimuth {@link AngleMeasurement} + */ + @NonNull + public AngleMeasurement getAzimuth() { + return mAzimuthAngleMeasurement; + } + + /** + * Altitude angle measurement + * <p>Altitude {@link AngleMeasurement} of remote device in horizontal coordinate system, this + * is the angle above the equator when the north pole is up. + * + * <p>See: https://en.wikipedia.org/wiki/Horizontal_coordinate_system + * + * <p>On an Android device, altitude is defined as the angle vertical from ground when holding + * the device in portrait mode upright. + * + * @return altitude {@link AngleMeasurement} or null when this is not available + */ + @Nullable + public AngleMeasurement getAltitude() { + return mAltitudeAngleMeasurement; + } + + /** + * Builder class for {@link AngleOfArrivalMeasurement}. + */ + public static final class Builder { + private AngleMeasurement mAzimuthAngleMeasurement = null; + private AngleMeasurement mAltitudeAngleMeasurement = null; + + /** + * Set the azimuth angle + * + * @param azimuthAngle azimuth angle + */ + public Builder setAzimuthAngleMeasurement(@NonNull AngleMeasurement azimuthAngle) { + mAzimuthAngleMeasurement = azimuthAngle; + return this; + } + + /** + * Set the altitude angle + * + * @param altitudeAngle altitude angle + */ + public Builder setAltitudeAngleMeasurement(@NonNull AngleMeasurement altitudeAngle) { + mAltitudeAngleMeasurement = altitudeAngle; + return this; + } + + /** + * Build the {@link AngleOfArrivalMeasurement} object + * + * @throws IllegalStateException if the required azimuth angle is not provided + */ + public AngleOfArrivalMeasurement build() { + if (mAzimuthAngleMeasurement == null) { + throw new IllegalStateException("Azimuth angle measurement is not set"); + } + + return new AngleOfArrivalMeasurement(mAzimuthAngleMeasurement, + mAltitudeAngleMeasurement); + } + } +} diff --git a/core/java/android/uwb/DistanceMeasurement.java b/core/java/android/uwb/DistanceMeasurement.java new file mode 100644 index 000000000000..4cd5d83f4efc --- /dev/null +++ b/core/java/android/uwb/DistanceMeasurement.java @@ -0,0 +1,145 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.uwb; + +import android.annotation.FloatRange; + +/** + * A data point for the distance measurement + * + * <p>The actual distance is interpreted as: + * {@link #getMeters()} +/- {@link #getErrorMeters()} at {@link #getConfidenceLevel()} + * + * @hide + */ +public final class DistanceMeasurement { + private final double mMeters; + private final double mErrorMeters; + private final double mConfidenceLevel; + + private DistanceMeasurement(double meters, double errorMeters, double confidenceLevel) { + mMeters = meters; + mErrorMeters = errorMeters; + mConfidenceLevel = confidenceLevel; + } + + /** + * Distance measurement in meters + * + * @return distance in meters + */ + public double getMeters() { + return mMeters; + } + + /** + * Error of distance measurement in meters + * <p>Must be positive + * + * @return error of distance measurement in meters + */ + public double getErrorMeters() { + return mErrorMeters; + } + + /** + * Distance measurement confidence level expressed as a value between 0.0 to 1.0. + * + * <p>A value of 0.0 indicates no confidence in the measurement. A value of 1.0 represents + * maximum confidence in the measurement + * + * @return confidence level + */ + @FloatRange(from = 0.0, to = 1.0) + public double getConfidenceLevel() { + return mConfidenceLevel; + } + + /** + * Builder to get a {@link DistanceMeasurement} object. + */ + public static final class Builder { + private double mMeters = Double.NaN; + private double mErrorMeters = Double.NaN; + private double mConfidenceLevel = Double.NaN; + + /** + * Set the distance measurement in meters + * + * @param meters distance in meters + * @throws IllegalArgumentException if meters is NaN + */ + public Builder setMeters(double meters) { + if (Double.isNaN(meters)) { + throw new IllegalArgumentException("meters cannot be NaN"); + } + mMeters = meters; + return this; + } + + /** + * Set the distance error in meters + * + * @param errorMeters distance error in meters + * @throws IllegalArgumentException if error is negative or NaN + */ + public Builder setErrorMeters(double errorMeters) { + if (Double.isNaN(errorMeters) || errorMeters < 0.0) { + throw new IllegalArgumentException( + "errorMeters must be >= 0.0 and not NaN: " + errorMeters); + } + mErrorMeters = errorMeters; + return this; + } + + /** + * Set the confidence level + * + * @param confidenceLevel the confidence level in the distance measurement + * @throws IllegalArgumentException if confidence level is not in the range of [0.0, 1.0] + */ + public Builder setConfidenceLevel(double confidenceLevel) { + if (confidenceLevel < 0.0 || confidenceLevel > 1.0) { + throw new IllegalArgumentException( + "confidenceLevel must be in the range [0.0, 1.0]: " + confidenceLevel); + } + mConfidenceLevel = confidenceLevel; + return this; + } + + /** + * Builds the {@link DistanceMeasurement} object + * + * @throws IllegalStateException if meters, error, or confidence are not set + */ + public DistanceMeasurement build() { + if (Double.isNaN(mMeters)) { + throw new IllegalStateException("Meters cannot be NaN"); + } + + if (Double.isNaN(mErrorMeters)) { + throw new IllegalStateException("Error meters cannot be NaN"); + } + + if (Double.isNaN(mConfidenceLevel)) { + throw new IllegalStateException("Confidence level cannot be NaN"); + } + + return new DistanceMeasurement(mMeters, mErrorMeters, mConfidenceLevel); + } + } +} diff --git a/core/java/android/uwb/RangingMeasurement.java b/core/java/android/uwb/RangingMeasurement.java new file mode 100644 index 000000000000..33a34e3954c2 --- /dev/null +++ b/core/java/android/uwb/RangingMeasurement.java @@ -0,0 +1,229 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.uwb; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.SuppressLint; +import android.os.SystemClock; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Representation of a ranging measurement between the local device and a remote device + * + * @hide + */ +public final class RangingMeasurement { + private final UwbAddress mRemoteDeviceAddress; + private final @Status int mStatus; + private final long mElapsedRealtimeNanos; + private final DistanceMeasurement mDistanceMeasurement; + private final AngleOfArrivalMeasurement mAngleOfArrivalMeasurement; + + private RangingMeasurement(@NonNull UwbAddress remoteDeviceAddress, @Status int status, + long elapsedRealtimeNanos, @Nullable DistanceMeasurement distanceMeasurement, + @Nullable AngleOfArrivalMeasurement angleOfArrivalMeasurement) { + mRemoteDeviceAddress = remoteDeviceAddress; + mStatus = status; + mElapsedRealtimeNanos = elapsedRealtimeNanos; + mDistanceMeasurement = distanceMeasurement; + mAngleOfArrivalMeasurement = angleOfArrivalMeasurement; + } + + /** + * Get the remote device's {@link UwbAddress} + * + * @return the remote device's {@link UwbAddress} + */ + @NonNull + public UwbAddress getRemoteDeviceAddress() { + return mRemoteDeviceAddress; + } + + @Retention(RetentionPolicy.SOURCE) + @IntDef(value = { + RANGING_STATUS_SUCCESS, + RANGING_STATUS_FAILURE_OUT_OF_RANGE, + RANGING_STATUS_FAILURE_UNKNOWN_ERROR}) + public @interface Status {} + + /** + * Ranging attempt was successful for this device + */ + public static final int RANGING_STATUS_SUCCESS = 0; + + /** + * Ranging failed for this device because it is out of range + */ + public static final int RANGING_STATUS_FAILURE_OUT_OF_RANGE = 1; + + /** + * Ranging failed for this device because of unknown error + */ + public static final int RANGING_STATUS_FAILURE_UNKNOWN_ERROR = -1; + + /** + * Get the status of this ranging measurement + * + * <p>Possible values are + * {@link #RANGING_STATUS_SUCCESS}, + * {@link #RANGING_STATUS_FAILURE_OUT_OF_RANGE}, + * {@link #RANGING_STATUS_FAILURE_UNKNOWN_ERROR}. + * + * @return the status of the ranging measurement + */ + @Status + public int getStatus() { + return mStatus; + } + + /** + * Timestamp of this ranging measurement in time since boot nanos in the same namespace as + * {@link SystemClock#elapsedRealtimeNanos()} + * + * @return timestamp of ranging measurement in nanoseconds + */ + @SuppressLint("MethodNameUnits") + public long getElapsedRealtimeNanos() { + return mElapsedRealtimeNanos; + } + + /** + * Get the distance measurement + * + * @return a {@link DistanceMeasurement} or null if {@link #getStatus()} != + * {@link #RANGING_STATUS_SUCCESS} + */ + @Nullable + public DistanceMeasurement getDistance() { + return mDistanceMeasurement; + } + + /** + * Get the angle of arrival measurement + * + * @return an {@link AngleOfArrivalMeasurement} or null if {@link #getStatus()} != + * {@link #RANGING_STATUS_SUCCESS} + */ + @Nullable + public AngleOfArrivalMeasurement getAngleOfArrival() { + return mAngleOfArrivalMeasurement; + } + + /** + * Builder for a {@link RangingMeasurement} object. + */ + public static final class Builder { + private UwbAddress mRemoteDeviceAddress = null; + private @Status int mStatus = RANGING_STATUS_FAILURE_UNKNOWN_ERROR; + private long mElapsedRealtimeNanos = -1L; + private DistanceMeasurement mDistanceMeasurement = null; + private AngleOfArrivalMeasurement mAngleOfArrivalMeasurement = null; + + /** + * Set the remote device address that this measurement is for + * + * @param remoteDeviceAddress remote device's address + */ + public Builder setRemoteDeviceAddress(@NonNull UwbAddress remoteDeviceAddress) { + mRemoteDeviceAddress = remoteDeviceAddress; + return this; + } + + /** + * Set the status of ranging measurement + * + * @param status the status of the ranging measurement + */ + public Builder setStatus(@Status int status) { + mStatus = status; + return this; + } + + /** + * Set the elapsed realtime in nanoseconds when the ranging measurement occurred + * + * @param elapsedRealtimeNanos time the ranging measurement occurred + */ + public Builder setElapsedRealtimeNanos(long elapsedRealtimeNanos) { + if (elapsedRealtimeNanos < 0) { + throw new IllegalArgumentException("elapsedRealtimeNanos must be >= 0"); + } + mElapsedRealtimeNanos = elapsedRealtimeNanos; + return this; + } + + /** + * Set the {@link DistanceMeasurement} + * + * @param distanceMeasurement the distance measurement for this ranging measurement + */ + public Builder setDistanceMeasurement(@NonNull DistanceMeasurement distanceMeasurement) { + mDistanceMeasurement = distanceMeasurement; + return this; + } + + /** + * Set the {@link AngleOfArrivalMeasurement} + * + * @param angleOfArrivalMeasurement the angle of arrival measurement for this ranging + * measurement + */ + public Builder setAngleOfArrivalMeasurement( + @NonNull AngleOfArrivalMeasurement angleOfArrivalMeasurement) { + mAngleOfArrivalMeasurement = angleOfArrivalMeasurement; + return this; + } + + /** + * Build the {@link RangingMeasurement} object + * + * @throws IllegalStateException if a distance or angle of arrival measurement is provided + * but the measurement was not successful, if the + * elapsedRealtimeNanos of the measurement is invalid, or + * if no remote device address is set + */ + public RangingMeasurement build() { + if (mStatus != RANGING_STATUS_SUCCESS) { + if (mDistanceMeasurement != null) { + throw new IllegalStateException( + "Distance Measurement must be null if ranging is not successful"); + } + + if (mAngleOfArrivalMeasurement != null) { + throw new IllegalStateException( + "Angle of Arrival must be null if ranging is not successful"); + } + } + + if (mRemoteDeviceAddress == null) { + throw new IllegalStateException("No remote device address was set"); + } + + if (mElapsedRealtimeNanos < 0) { + throw new IllegalStateException( + "elapsedRealtimeNanos must be >=0: " + mElapsedRealtimeNanos); + } + + return new RangingMeasurement(mRemoteDeviceAddress, mStatus, mElapsedRealtimeNanos, + mDistanceMeasurement, mAngleOfArrivalMeasurement); + } + } +} diff --git a/core/java/android/uwb/RangingParams.java b/core/java/android/uwb/RangingParams.java new file mode 100644 index 000000000000..a50de3e61425 --- /dev/null +++ b/core/java/android/uwb/RangingParams.java @@ -0,0 +1,381 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.uwb; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.os.PersistableBundle; +import android.util.Duration; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * An object used when requesting to open a new {@link RangingSession}. + * <p>Use {@link RangingParams.Builder} to create an instance of this class. + * + * @hide + */ +public final class RangingParams { + private final boolean mIsInitiator; + private final boolean mIsController; + private final Duration mSamplePeriod; + private final UwbAddress mLocalDeviceAddress; + private final List<UwbAddress> mRemoteDeviceAddresses; + private final int mChannelNumber; + private final int mTransmitPreambleCodeIndex; + private final int mReceivePreambleCodeIndex; + private final int mStsPhyPacketType; + private final PersistableBundle mSpecificationParameters; + + private RangingParams(boolean isInitiator, boolean isController, + @NonNull Duration samplingPeriod, @NonNull UwbAddress localDeviceAddress, + @NonNull List<UwbAddress> remoteDeviceAddresses, int channelNumber, + int transmitPreambleCodeIndex, int receivePreambleCodeIndex, + @StsPhyPacketType int stsPhyPacketType, + @NonNull PersistableBundle specificationParameters) { + mIsInitiator = isInitiator; + mIsController = isController; + mSamplePeriod = samplingPeriod; + mLocalDeviceAddress = localDeviceAddress; + mRemoteDeviceAddresses = remoteDeviceAddresses; + mChannelNumber = channelNumber; + mTransmitPreambleCodeIndex = transmitPreambleCodeIndex; + mReceivePreambleCodeIndex = receivePreambleCodeIndex; + mStsPhyPacketType = stsPhyPacketType; + mSpecificationParameters = specificationParameters; + } + + /** + * Get if the local device is the initiator + * + * @return true if the device is the initiator + */ + public boolean isInitiator() { + return mIsInitiator; + } + + /** + * Get if the local device is the controller + * + * @return true if the device is the controller + */ + public boolean isController() { + return mIsController; + } + + /** + * The desired amount of time between two adjacent samples of measurement + * + * @return the ranging sample period + */ + @NonNull + public Duration getSamplingPeriod() { + return mSamplePeriod; + } + + /** + * Local device's {@link UwbAddress} + * + * <p>Simultaneous {@link RangingSession}s on the same device can have different results for + * {@link #getLocalDeviceAddress()}. + * + * @return the local device's {@link UwbAddress} + */ + @NonNull + public UwbAddress getLocalDeviceAddress() { + return mLocalDeviceAddress; + } + + /** + * Gets a list of all remote device's {@link UwbAddress} + * + * @return a {@link List} of {@link UwbAddress} representing the remote devices + */ + @NonNull + public List<UwbAddress> getRemoteDeviceAddresses() { + return mRemoteDeviceAddresses; + } + + /** + * Channel number used between this device pair as defined by 802.15.4z + * + * Range: -1, 0-15 + * + * @return the channel to use + */ + public int getChannelNumber() { + return mChannelNumber; + } + + /** + * Preamble index used between this device pair as defined by 802.15.4z + * + * Range: 0, 0-32 + * + * @return the preamble index to use for transmitting + */ + public int getTxPreambleIndex() { + return mTransmitPreambleCodeIndex; + } + + /** + * preamble index used between this device pair as defined by 802.15.4z + * + * Range: 0, 13-16, 21-32 + * + * @return the preamble index to use for receiving + */ + public int getRxPreambleIndex() { + return mReceivePreambleCodeIndex; + } + + @Retention(RetentionPolicy.SOURCE) + @IntDef(value = { + STS_PHY_PACKET_TYPE_SP0, + STS_PHY_PACKET_TYPE_SP1, + STS_PHY_PACKET_TYPE_SP2, + STS_PHY_PACKET_TYPE_SP3}) + public @interface StsPhyPacketType {} + + /** + * PHY packet type SP0 when STS is used as defined by 802.15.4z + */ + public static final int STS_PHY_PACKET_TYPE_SP0 = 0; + + /** + * PHY packet type SP1 when STS is used as defined by 802.15.4z + */ + public static final int STS_PHY_PACKET_TYPE_SP1 = 1; + + /** + * PHY packet type SP2 when STS is used as defined by 802.15.4z + */ + public static final int STS_PHY_PACKET_TYPE_SP2 = 2; + + /** + * PHY packet type SP3 when STS is used as defined by 802.15.4z + */ + public static final int STS_PHY_PACKET_TYPE_SP3 = 3; + + /** + * Get the type of PHY packet when STS is used as defined by 802.15.4z + * + * @return the {@link StsPhyPacketType} to use + */ + @StsPhyPacketType + public int getStsPhyPacketType() { + return mStsPhyPacketType; + } + + /** + * Parameters for a specific UWB protocol constructed using a support library. + * + * <p>Android reserves the '^android.*' namespace + * + * @return a {@link PersistableBundle} copy of protocol specific parameters + */ + public @Nullable PersistableBundle getSpecificationParameters() { + return new PersistableBundle(mSpecificationParameters); + } + + /** + * Builder class for {@link RangingParams}. + */ + public static final class Builder { + private boolean mIsInitiator = false; + private boolean mIsController = false; + private Duration mSamplePeriod = null; + private UwbAddress mLocalDeviceAddress = null; + private List<UwbAddress> mRemoteDeviceAddresses = new ArrayList<>(); + private int mChannelNumber = 0; + private int mTransmitPreambleCodeIndex = 0; + private int mReceivePreambleCodeIndex = 0; + private int mStsPhyPacketType = STS_PHY_PACKET_TYPE_SP0; + private PersistableBundle mSpecificationParameters = new PersistableBundle(); + + /** + * Set whether the device is the initiator or responder as defined by IEEE 802.15.4z + * + * @param isInitiator whether the device is the initiator (true) or responder (false) + */ + public Builder setIsInitiator(boolean isInitiator) { + mIsInitiator = isInitiator; + return this; + } + + /** + * Set whether the local device is the controller or controlee as defined by IEEE 802.15.4z + * + * @param isController whether the device is the controller (true) or controlee (false) + */ + public Builder setIsController(boolean isController) { + mIsController = isController; + return this; + } + + /** + * Set the time between ranging samples + * + * @param samplePeriod the time between ranging samples + */ + public Builder setSamplePeriod(@NonNull Duration samplePeriod) { + mSamplePeriod = samplePeriod; + return this; + } + + /** + * Set the local device address + * + * @param localDeviceAddress the local device's address for the {@link RangingSession} + */ + public Builder setLocalDeviceAddress(@NonNull UwbAddress localDeviceAddress) { + mLocalDeviceAddress = localDeviceAddress; + return this; + } + + /** + * Add a remote device's address to the ranging session + * + * @param remoteDeviceAddress a remote device's address for the {@link RangingSession} + * @throws IllegalArgumentException if {@code remoteDeviceAddress} is already present. + */ + public Builder addRemoteDeviceAddress(@NonNull UwbAddress remoteDeviceAddress) { + if (mRemoteDeviceAddresses.contains(remoteDeviceAddress)) { + throw new IllegalArgumentException( + "Remote device address already added: " + remoteDeviceAddress.toString()); + } + mRemoteDeviceAddresses.add(remoteDeviceAddress); + return this; + } + + /** + * Set the IEEE 802.15.4z channel to use for the {@link RangingSession} + * <p>Valid values are in the range [-1, 15] + * + * @param channelNumber the channel to use for the {@link RangingSession} + * @throws IllegalArgumentException if {@code channelNumber} is invalid. + */ + public Builder setChannelNumber(int channelNumber) { + if (channelNumber < -1 || channelNumber > 15) { + throw new IllegalArgumentException("Invalid channel number"); + } + mChannelNumber = channelNumber; + return this; + } + + private static final Set<Integer> VALID_TX_PREAMBLE_CODES = new HashSet<Integer>( + Arrays.asList(0, 13, 14, 15, 16, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32)); + + /** + * Set the IEEE 802.15.4z preamble code index to use when transmitting + * + * <p>Valid values are in the ranges: [0], [13-16], [21-32] + * + * @param transmitPreambleCodeIndex preamble code index to use for transmitting + * @throws IllegalArgumentException if {@code transmitPreambleCodeIndex} is invalid. + */ + public Builder setTransmitPreambleCodeIndex(int transmitPreambleCodeIndex) { + if (!VALID_TX_PREAMBLE_CODES.contains(transmitPreambleCodeIndex)) { + throw new IllegalArgumentException( + "Invalid transmit preamble: " + transmitPreambleCodeIndex); + } + mTransmitPreambleCodeIndex = transmitPreambleCodeIndex; + return this; + } + + private static final Set<Integer> VALID_RX_PREAMBLE_CODES = new HashSet<Integer>( + Arrays.asList(0, 16, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32)); + + /** + * Set the IEEE 802.15.4z preamble code index to use when receiving + * + * Valid values are in the ranges: [0], [16-32] + * + * @param receivePreambleCodeIndex preamble code index to use for receiving + * @throws IllegalArgumentException if {@code receivePreambleCodeIndex} is invalid. + */ + public Builder setReceivePreambleCodeIndex(int receivePreambleCodeIndex) { + if (!VALID_RX_PREAMBLE_CODES.contains(receivePreambleCodeIndex)) { + throw new IllegalArgumentException( + "Invalid receive preamble: " + receivePreambleCodeIndex); + } + mReceivePreambleCodeIndex = receivePreambleCodeIndex; + return this; + } + + /** + * Set the IEEE 802.15.4z PHY packet type when STS is used + * + * @param stsPhyPacketType PHY packet type when STS is used + * @throws IllegalArgumentException if {@code stsPhyPacketType} is invalid. + */ + public Builder setStsPhPacketType(@StsPhyPacketType int stsPhyPacketType) { + if (stsPhyPacketType != STS_PHY_PACKET_TYPE_SP0 + && stsPhyPacketType != STS_PHY_PACKET_TYPE_SP1 + && stsPhyPacketType != STS_PHY_PACKET_TYPE_SP2 + && stsPhyPacketType != STS_PHY_PACKET_TYPE_SP3) { + throw new IllegalArgumentException("unknown StsPhyPacketType: " + stsPhyPacketType); + } + + mStsPhyPacketType = stsPhyPacketType; + return this; + } + + /** + * Set the specification parameters + * + * <p>Creates a copy of the parameters + * + * @param parameters specification parameters built from support library + */ + public Builder setSpecificationParameters(@NonNull PersistableBundle parameters) { + mSpecificationParameters = new PersistableBundle(parameters); + return this; + } + + /** + * Build the {@link RangingParams} object. + * + * @throws IllegalStateException if required parameters are missing + */ + public RangingParams build() { + if (mSamplePeriod == null) { + throw new IllegalStateException("No sample period provided"); + } + + if (mLocalDeviceAddress == null) { + throw new IllegalStateException("Local device address not provided"); + } + + if (mRemoteDeviceAddresses.size() == 0) { + throw new IllegalStateException("No remote device address(es) provided"); + } + + return new RangingParams(mIsInitiator, mIsController, mSamplePeriod, + mLocalDeviceAddress, mRemoteDeviceAddresses, mChannelNumber, + mTransmitPreambleCodeIndex, mReceivePreambleCodeIndex, mStsPhyPacketType, + mSpecificationParameters); + } + } +} diff --git a/core/java/android/uwb/RangingReport.java b/core/java/android/uwb/RangingReport.java new file mode 100644 index 000000000000..5aca12aaf2cf --- /dev/null +++ b/core/java/android/uwb/RangingReport.java @@ -0,0 +1,101 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.uwb; + +import android.annotation.NonNull; + +import java.util.ArrayList; +import java.util.List; + +/** + * This class contains the UWB ranging data + * + * @hide + */ +public final class RangingReport { + private final List<RangingMeasurement> mRangingMeasurements; + + private RangingReport(@NonNull List<RangingMeasurement> rangingMeasurements) { + mRangingMeasurements = rangingMeasurements; + } + + /** + * Get a {@link List} of {@link RangingMeasurement} objects in the last measurement interval + * <p>The underlying UWB adapter may choose to do multiple measurements in each ranging + * interval. + * + * <p>The entries in the {@link List} are ordered in ascending order based on + * {@link RangingMeasurement#getElapsedRealtimeNanos()} + * + * @return a {@link List} of {@link RangingMeasurement} objects + */ + @NonNull + public List<RangingMeasurement> getMeasurements() { + return mRangingMeasurements; + } + + /** + * Builder for {@link RangingReport} object + */ + public static final class Builder { + List<RangingMeasurement> mMeasurements = new ArrayList<>(); + + /** + * Add a single {@link RangingMeasurement} + * + * @param rangingMeasurement a ranging measurement + */ + public Builder addMeasurement(@NonNull RangingMeasurement rangingMeasurement) { + mMeasurements.add(rangingMeasurement); + return this; + } + + /** + * Add a {@link List} of {@link RangingMeasurement}s + * + * @param rangingMeasurements {@link List} of {@link RangingMeasurement}s to add + */ + public Builder addMeasurements(@NonNull List<RangingMeasurement> rangingMeasurements) { + mMeasurements.addAll(rangingMeasurements); + return this; + } + + /** + * Build the {@link RangingReport} object + * + * @throws IllegalStateException if measurements are not in monotonically increasing order + */ + public RangingReport build() { + // Verify that all measurement timestamps are monotonically increasing + RangingMeasurement prevMeasurement = null; + for (int curIndex = 0; curIndex < mMeasurements.size(); curIndex++) { + RangingMeasurement curMeasurement = mMeasurements.get(curIndex); + if (prevMeasurement != null + && (prevMeasurement.getElapsedRealtimeNanos() + > curMeasurement.getElapsedRealtimeNanos())) { + throw new IllegalStateException( + "Timestamp (" + curMeasurement.getElapsedRealtimeNanos() + + ") at index " + curIndex + " is less than previous timestamp (" + + prevMeasurement.getElapsedRealtimeNanos() + ")"); + } + prevMeasurement = curMeasurement; + } + return new RangingReport(mMeasurements); + } + } +} + diff --git a/core/java/android/uwb/RangingSession.java b/core/java/android/uwb/RangingSession.java new file mode 100644 index 000000000000..f4033fe0d29e --- /dev/null +++ b/core/java/android/uwb/RangingSession.java @@ -0,0 +1,148 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.uwb; + +import android.annotation.IntDef; +import android.os.PersistableBundle; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.concurrent.Executor; + +/** + * This class provides a way to control an active UWB ranging session. + * <p>It also defines the required {@link RangingSession.Callback} that must be implemented + * in order to be notified of UWB ranging results and status events related to the + * {@link RangingSession}. + * + * <p>To get an instance of {@link RangingSession}, first use + * {@link UwbManager#openRangingSession(RangingParams, Executor, Callback)} to request to open a + * session. Once the session is opened, a {@link RangingSession} object is provided through + * {@link RangingSession.Callback#onOpenSuccess(RangingSession, PersistableBundle)}. If opening a + * session fails, the failure is reported through {@link RangingSession.Callback#onClosed(int)} with + * the failure reason. + * + * @hide + */ +public final class RangingSession implements AutoCloseable { + /** + * Interface for receiving {@link RangingSession} events + */ + public interface Callback { + /** + * Invoked when {@link UwbManager#openRangingSession(RangingParams, Executor, Callback)} + * is successful + * + * @param session the newly opened {@link RangingSession} + * @param sessionInfo session specific parameters from lower layers + */ + void onOpenSuccess(RangingSession session, PersistableBundle sessionInfo); + + @Retention(RetentionPolicy.SOURCE) + @IntDef(value = { + CLOSE_REASON_UNKNOWN, + CLOSE_REASON_LOCAL_CLOSE_API, + CLOSE_REASON_LOCAL_BAD_PARAMETERS, + CLOSE_REASON_LOCAL_GENERIC_ERROR, + CLOSE_REASON_LOCAL_MAX_SESSIONS_REACHED, + CLOSE_REASON_LOCAL_SYSTEM_POLICY, + CLOSE_REASON_REMOTE_GENERIC_ERROR, + CLOSE_REASON_REMOTE_REQUEST}) + @interface CloseReason {} + + /** + * Indicates that the session was closed or failed to open due to an unknown reason + */ + int CLOSE_REASON_UNKNOWN = 0; + + /** + * Indicates that the session was closed or failed to open because + * {@link AutoCloseable#close()} or {@link RangingSession#close()} was called + */ + int CLOSE_REASON_LOCAL_CLOSE_API = 1; + + /** + * Indicates that the session failed to open due to erroneous parameters passed + * to {@link UwbManager#openRangingSession(RangingParams, Executor, Callback)} + */ + int CLOSE_REASON_LOCAL_BAD_PARAMETERS = 2; + + /** + * Indicates that the session was closed due to some local error on this device besides the + * error code already listed + */ + int CLOSE_REASON_LOCAL_GENERIC_ERROR = 3; + + /** + * Indicates that the session failed to open because the number of currently open sessions + * is equal to {@link UwbManager#getMaxSimultaneousSessions()} + */ + int CLOSE_REASON_LOCAL_MAX_SESSIONS_REACHED = 4; + + /** + * Indicates that the session was closed or failed to open due to local system policy, such + * as privacy policy, power management policy, permissions, and more. + */ + int CLOSE_REASON_LOCAL_SYSTEM_POLICY = 5; + + /** + * Indicates that the session was closed or failed to open due to an error with the remote + * device besides error codes already listed. + */ + int CLOSE_REASON_REMOTE_GENERIC_ERROR = 6; + + /** + * Indicates that the session was closed or failed to open due to an explicit request from + * the remote device. + */ + int CLOSE_REASON_REMOTE_REQUEST = 7; + + /** + * Invoked when session is either closed spontaneously, or per user request via + * {@link RangingSession#close()} or {@link AutoCloseable#close()}, or when session failed + * to open. + * + * @param reason reason for the session closure + */ + void onClosed(@CloseReason int reason); + + /** + * Called once per ranging interval even when a ranging measurement fails + * + * @param rangingReport ranging report for this interval's measurements + */ + void onReportReceived(RangingReport rangingReport); + } + + /** + * Close the ranging session + * <p>If this session is currently open, it will close and stop the session. + * <p>If the session is in the process of being opened, it will attempt to stop the session from + * being opened. + * <p>If the session is already closed, the registered {@link Callback#onClosed(int)} callback + * will still be invoked. + * + * <p>{@link Callback#onClosed(int)} will be invoked using the same callback + * object given to {@link UwbManager#openRangingSession(RangingParams, Executor, Callback)} when + * the {@link RangingSession} was opened. The callback will be invoked after each call to + * {@link #close()}, even if the {@link RangingSession} is already closed. + */ + @Override + public void close() { + throw new UnsupportedOperationException(); + } +} diff --git a/core/java/android/uwb/UwbAddress.java b/core/java/android/uwb/UwbAddress.java new file mode 100644 index 000000000000..48fcb10e1a1a --- /dev/null +++ b/core/java/android/uwb/UwbAddress.java @@ -0,0 +1,82 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.uwb; + +import android.annotation.NonNull; +import android.annotation.Nullable; + +/** + * A class representing a UWB address + * + * @hide + */ +public final class UwbAddress { + public static final int SHORT_ADDRESS_BYTE_LENGTH = 2; + public static final int EXTENDED_ADDRESS_BYTE_LENGTH = 8; + + /** + * Create a {@link UwbAddress} from a byte array. + * + * <p>If the provided array is {@link #SHORT_ADDRESS_BYTE_LENGTH} bytes, a short address is + * created. If the provided array is {@link #EXTENDED_ADDRESS_BYTE_LENGTH} bytes, then an + * extended address is created. + * + * @param address a byte array to convert to a {@link UwbAddress} + * @return a {@link UwbAddress} created from the input byte array + * @throw IllegableArumentException when the length is not one of + * {@link #SHORT_ADDRESS_BYTE_LENGTH} or {@link #EXTENDED_ADDRESS_BYTE_LENGTH} bytes + */ + @NonNull + public static UwbAddress fromBytes(byte[] address) throws IllegalArgumentException { + throw new UnsupportedOperationException(); + } + + /** + * Get the address as a byte array + * + * @return the byte representation of this {@link UwbAddress} + */ + @NonNull + public byte[] toBytes() { + throw new UnsupportedOperationException(); + } + + /** + * The length of the address in bytes + * <p>Possible values are {@link #SHORT_ADDRESS_BYTE_LENGTH} and + * {@link #EXTENDED_ADDRESS_BYTE_LENGTH}. + */ + public int size() { + throw new UnsupportedOperationException(); + } + + @NonNull + @Override + public String toString() { + throw new UnsupportedOperationException(); + } + + @Override + public boolean equals(@Nullable Object obj) { + throw new UnsupportedOperationException(); + } + + @Override + public int hashCode() { + throw new UnsupportedOperationException(); + } +} diff --git a/core/java/android/uwb/UwbManager.java b/core/java/android/uwb/UwbManager.java index 8097dc6dca11..d58d5bfd8de3 100644 --- a/core/java/android/uwb/UwbManager.java +++ b/core/java/android/uwb/UwbManager.java @@ -266,4 +266,33 @@ public final class UwbManager { public int getMaxRemoteDevicesPerResponderSession() { throw new UnsupportedOperationException(); } + + /** + * Open a {@link RangingSession} with the given parameters + * <p>This function is asynchronous and will return before ranging begins. The + * {@link RangingSession.Callback#onOpenSuccess(RangingSession, PersistableBundle)} function is + * called with a {@link RangingSession} object used to control ranging when the session is + * successfully opened. + * + * <p>If a session cannot be opened, then {@link RangingSession.Callback#onClosed(int)} will be + * invoked with the appropriate {@link RangingSession.Callback.CloseReason}. + * + * <p>An open {@link RangingSession} will be automatically closed if client application process + * dies. + * + * @param params {@link RangingParams} used to initialize this {@link RangingSession} + * @param executor {@link Executor} to run callbacks + * @param callbacks {@link RangingSession.Callback} to associate with the + * {@link RangingSession} that is being opened. + * + * @return an {@link AutoCloseable} that is able to be used to close or cancel the opening of a + * {@link RangingSession} that has been requested through {@link #openRangingSession} + * but has not yet been made available by + * {@link RangingSession.Callback#onOpenSuccess}. + */ + @NonNull + public AutoCloseable openRangingSession(@NonNull RangingParams params, + @NonNull Executor executor, @NonNull RangingSession.Callback callbacks) { + throw new UnsupportedOperationException(); + } } diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java index c4048e5a032d..34e8221bc971 100644 --- a/core/java/android/view/Display.java +++ b/core/java/android/view/Display.java @@ -1466,7 +1466,7 @@ public final class Display { } @Override - public boolean equals(Object other) { + public boolean equals(@Nullable Object other) { if (this == other) { return true; } @@ -1620,7 +1620,7 @@ public final class Display { } @Override - public boolean equals(Object other) { + public boolean equals(@Nullable Object other) { if (this == other) { return true; } diff --git a/core/java/android/view/DisplayAddress.java b/core/java/android/view/DisplayAddress.java index 92f1adcd928b..91a24c64d37c 100644 --- a/core/java/android/view/DisplayAddress.java +++ b/core/java/android/view/DisplayAddress.java @@ -110,7 +110,7 @@ public abstract class DisplayAddress implements Parcelable { } @Override - public boolean equals(Object other) { + public boolean equals(@Nullable Object other) { return other instanceof Physical && mPhysicalDisplayId == ((Physical) other).mPhysicalDisplayId; } @@ -171,7 +171,7 @@ public abstract class DisplayAddress implements Parcelable { private final String mMacAddress; @Override - public boolean equals(Object other) { + public boolean equals(@Nullable Object other) { return other instanceof Network && mMacAddress.equals(((Network) other).mMacAddress); } diff --git a/core/java/android/view/DisplayAdjustments.java b/core/java/android/view/DisplayAdjustments.java index 7c01f7a8739a..5d5771c9148d 100644 --- a/core/java/android/view/DisplayAdjustments.java +++ b/core/java/android/view/DisplayAdjustments.java @@ -168,7 +168,7 @@ public class DisplayAdjustments { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (!(o instanceof DisplayAdjustments)) { return false; } @@ -220,7 +220,7 @@ public class DisplayAdjustments { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (!(o instanceof FixedRotationAdjustments)) { return false; } diff --git a/core/java/android/view/DisplayCutout.java b/core/java/android/view/DisplayCutout.java index b4863f9c6dd0..3f2dd4d8f557 100644 --- a/core/java/android/view/DisplayCutout.java +++ b/core/java/android/view/DisplayCutout.java @@ -205,7 +205,7 @@ public final class DisplayCutout { return result; } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (o == this) { return true; } @@ -535,7 +535,7 @@ public final class DisplayCutout { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (o == this) { return true; } @@ -877,7 +877,7 @@ public final class DisplayCutout { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { return o instanceof ParcelableWrapper && mInner.equals(((ParcelableWrapper) o).mInner); } diff --git a/core/java/android/view/DisplayInfo.java b/core/java/android/view/DisplayInfo.java index b1ede4102bec..fe9a1a76bbaf 100644 --- a/core/java/android/view/DisplayInfo.java +++ b/core/java/android/view/DisplayInfo.java @@ -295,7 +295,7 @@ public final class DisplayInfo implements Parcelable { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { return o instanceof DisplayInfo && equals((DisplayInfo)o); } diff --git a/core/java/android/view/IScrollCaptureController.aidl b/core/java/android/view/IScrollCaptureCallbacks.aidl index 8474a00b302f..d97e3c66cc5d 100644 --- a/core/java/android/view/IScrollCaptureController.aidl +++ b/core/java/android/view/IScrollCaptureCallbacks.aidl @@ -20,32 +20,31 @@ import android.graphics.Point; import android.graphics.Rect; import android.view.Surface; -import android.view.IScrollCaptureClient; +import android.view.IScrollCaptureConnection; /** - * Interface to a controller passed to the {@link IScrollCaptureClient} which provides the client an - * asynchronous callback channel for responses. + * Asynchronous callback channel for responses to scroll capture requests. * * {@hide} */ -interface IScrollCaptureController { +interface IScrollCaptureCallbacks { /** - * Scroll capture is available, and a client connect has been returned. + * Scroll capture is available, and a connection has been provided. * - * @param client interface to a ScrollCaptureCallback in the window process + * @param connection a connection to a window process and scrollable content * @param scrollAreaInWindow the location of scrolling in global (window) coordinate space */ - oneway void onClientConnected(in IScrollCaptureClient client, in Rect scrollBounds, + oneway void onConnected(in IScrollCaptureConnection connection, in Rect scrollBounds, in Point positionInWindow); /** - * Nothing in the window can be scrolled, scroll capture not offered. + * The window does not support scroll capture. */ - oneway void onClientUnavailable(); + oneway void onUnavailable(); /** - * Notifies the system that the client has confirmed the request and is ready to begin providing - * image requests. + * Called when the remote end has confirmed the request and is ready to begin providing image + * requests. */ oneway void onCaptureStarted(); diff --git a/core/java/android/view/IScrollCaptureClient.aidl b/core/java/android/view/IScrollCaptureConnection.aidl index 5f135a37dfef..63a4f48aeb20 100644 --- a/core/java/android/view/IScrollCaptureClient.aidl +++ b/core/java/android/view/IScrollCaptureConnection.aidl @@ -26,7 +26,7 @@ import android.view.Surface; * * {@hide} */ -interface IScrollCaptureClient { +interface IScrollCaptureConnection { /** * Informs the client that it has been selected for scroll capture and should prepare to diff --git a/core/java/android/view/IWindow.aidl b/core/java/android/view/IWindow.aidl index 193e674dd1b0..e685b30421be 100644 --- a/core/java/android/view/IWindow.aidl +++ b/core/java/android/view/IWindow.aidl @@ -26,7 +26,7 @@ import android.view.DisplayCutout; import android.view.DragEvent; import android.view.InsetsSourceControl; import android.view.InsetsState; -import android.view.IScrollCaptureController; +import android.view.IScrollCaptureCallbacks; import android.view.KeyEvent; import android.view.MotionEvent; import android.window.ClientWindowFrames; @@ -137,7 +137,7 @@ oneway interface IWindow { /** * Called when Scroll Capture support is requested for a window. * - * @param controller the controller to receive responses + * @param callbacks to receive responses */ - void requestScrollCapture(in IScrollCaptureController controller); + void requestScrollCapture(in IScrollCaptureCallbacks callbacks); } diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl index daab70ae336f..3c5d336b840d 100644 --- a/core/java/android/view/IWindowManager.aidl +++ b/core/java/android/view/IWindowManager.aidl @@ -42,7 +42,7 @@ import android.view.IDisplayFoldListener; import android.view.IDisplayWindowRotationController; import android.view.IOnKeyguardExitResult; import android.view.IPinnedStackListener; -import android.view.IScrollCaptureController; +import android.view.IScrollCaptureCallbacks; import android.view.RemoteAnimationAdapter; import android.view.IRotationWatcher; import android.view.ISystemGestureExclusionListener; @@ -337,11 +337,16 @@ interface IWindowManager */ boolean isDisplayRotationFrozen(int displayId); - /** + /** * Sets if display rotation is fixed to user specified value for given displayId. */ void setFixedToUserRotation(int displayId, int fixedToUserRotation); + /** + * Sets if all requested fixed orientation should be ignored for given displayId. + */ + void setIgnoreOrientationRequest(int displayId, boolean ignoreOrientationRequest); + /** * Screenshot the current wallpaper layer, including the whole screen. */ @@ -746,12 +751,10 @@ interface IWindowManager * @param behindClient token for a window, used to filter the search to windows behind it, or * {@code null} to accept a window at any zOrder * @param taskId specifies the id of a task the result must belong to, or -1 to ignore task ids - * @param controller the controller to receive results, a call to either - * {@link IScrollCaptureController#onClientConnected} or - * {@link IScrollCaptureController#onClientUnavailable}. + * @param callbacks the object to receive replies */ void requestScrollCapture(int displayId, IBinder behindClient, int taskId, - IScrollCaptureController controller); + IScrollCaptureCallbacks callbacks); /** * Holds the WM lock for the specified amount of milliseconds. diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl index 69a5fafbb0db..7f36169ada50 100644 --- a/core/java/android/view/IWindowSession.aidl +++ b/core/java/android/view/IWindowSession.aidl @@ -45,12 +45,13 @@ import java.util.List; */ interface IWindowSession { int addToDisplay(IWindow window, in WindowManager.LayoutParams attrs, - in int viewVisibility, in int layerStackId, out Rect outFrame, - out Rect outContentInsets, out Rect outStableInsets, + in int viewVisibility, in int layerStackId, in InsetsState requestedVisibility, + out Rect outFrame, out Rect outContentInsets, out Rect outStableInsets, out DisplayCutout.ParcelableWrapper displayCutout, out InputChannel outInputChannel, out InsetsState insetsState, out InsetsSourceControl[] activeControls); int addToDisplayAsUser(IWindow window, in WindowManager.LayoutParams attrs, in int viewVisibility, in int layerStackId, in int userId, + in InsetsState requestedVisibility, out Rect outFrame, out Rect outContentInsets, out Rect outStableInsets, out DisplayCutout.ParcelableWrapper displayCutout, out InputChannel outInputChannel, out InsetsState insetsState, out InsetsSourceControl[] activeControls); @@ -98,9 +99,6 @@ interface IWindowSession { * @param outSurface Object in which is placed the new display surface. * @param insetsState The current insets state in the system. * @param outSurfaceSize The width and height of the surface control - * @param outBlastSurfaceControl A BLAST SurfaceControl allocated by the WindowManager - * the SurfaceControl willl be managed by the client side, but the WindowManager - * may use it as a deferTransaction barrier. * * @return int Result flags: {@link WindowManagerGlobal#RELAYOUT_SHOW_FOCUS}, * {@link WindowManagerGlobal#RELAYOUT_FIRST_TIME}. @@ -110,7 +108,7 @@ interface IWindowSession { int flags, long frameNumber, out ClientWindowFrames outFrames, out MergedConfiguration outMergedConfiguration, out SurfaceControl outSurfaceControl, out InsetsState insetsState, out InsetsSourceControl[] activeControls, - out Point outSurfaceSize, out SurfaceControl outBlastSurfaceControl); + out Point outSurfaceSize); /* * Notify the window manager that an application is relaunching and diff --git a/core/java/android/view/InsetsAnimationControlImpl.java b/core/java/android/view/InsetsAnimationControlImpl.java index 71899fab554c..878583be87c9 100644 --- a/core/java/android/view/InsetsAnimationControlImpl.java +++ b/core/java/android/view/InsetsAnimationControlImpl.java @@ -212,21 +212,21 @@ public class InsetsAnimationControlImpl implements WindowInsetsAnimationControll /** * @return Whether the finish callback of this animation should be invoked. */ - public boolean applyChangeInsets(InsetsState state) { + public boolean applyChangeInsets(@Nullable InsetsState outState) { if (mCancelled) { if (DEBUG) Log.d(TAG, "applyChangeInsets canceled"); return false; } final Insets offset = Insets.subtract(mShownInsets, mPendingInsets); ArrayList<SurfaceParams> params = new ArrayList<>(); - updateLeashesForSide(ISIDE_LEFT, offset.left, mShownInsets.left, mPendingInsets.left, - params, state, mPendingAlpha); - updateLeashesForSide(ISIDE_TOP, offset.top, mShownInsets.top, mPendingInsets.top, params, - state, mPendingAlpha); - updateLeashesForSide(ISIDE_RIGHT, offset.right, mShownInsets.right, mPendingInsets.right, - params, state, mPendingAlpha); - updateLeashesForSide(ISIDE_BOTTOM, offset.bottom, mShownInsets.bottom, - mPendingInsets.bottom, params, state, mPendingAlpha); + updateLeashesForSide(ISIDE_LEFT, offset.left, mPendingInsets.left, params, outState, + mPendingAlpha); + updateLeashesForSide(ISIDE_TOP, offset.top, mPendingInsets.top, params, outState, + mPendingAlpha); + updateLeashesForSide(ISIDE_RIGHT, offset.right, mPendingInsets.right, params, outState, + mPendingAlpha); + updateLeashesForSide(ISIDE_BOTTOM, offset.bottom, mPendingInsets.bottom, params, outState, + mPendingAlpha); mController.applySurfaceParams(params.toArray(new SurfaceParams[params.size()])); mCurrentInsets = mPendingInsets; @@ -357,8 +357,8 @@ public class InsetsAnimationControlImpl implements WindowInsetsAnimationControll return alpha >= 1 ? 1 : (alpha <= 0 ? 0 : alpha); } - private void updateLeashesForSide(@InternalInsetsSide int side, int offset, int maxInset, - int inset, ArrayList<SurfaceParams> surfaceParams, InsetsState state, Float alpha) { + private void updateLeashesForSide(@InternalInsetsSide int side, int offset, int inset, + ArrayList<SurfaceParams> surfaceParams, @Nullable InsetsState outState, float alpha) { ArraySet<InsetsSourceControl> items = mSideSourceMap.get(side); if (items == null) { return; @@ -377,8 +377,10 @@ public class InsetsAnimationControlImpl implements WindowInsetsAnimationControll ? (mAnimationType == ANIMATION_TYPE_SHOW ? true : !mFinished) : inset != 0; - state.getSource(source.getType()).setVisible(visible); - state.getSource(source.getType()).setFrame(mTmpFrame); + if (outState != null) { + outState.getSource(source.getType()).setVisible(visible); + outState.getSource(source.getType()).setFrame(mTmpFrame); + } // If the system is controlling the insets source, the leash can be null. if (leash != null) { diff --git a/core/java/android/view/InsetsAnimationThreadControlRunner.java b/core/java/android/view/InsetsAnimationThreadControlRunner.java index cc3cd278b267..1307052a25cc 100644 --- a/core/java/android/view/InsetsAnimationThreadControlRunner.java +++ b/core/java/android/view/InsetsAnimationThreadControlRunner.java @@ -44,7 +44,6 @@ public class InsetsAnimationThreadControlRunner implements InsetsAnimationContro private final InsetsAnimationControlImpl mControl; private final InsetsAnimationControlCallbacks mOuterCallbacks; private final Handler mMainThreadHandler; - private final InsetsState mState = new InsetsState(); private final InsetsAnimationControlCallbacks mCallbacks = new InsetsAnimationControlCallbacks() { @@ -60,7 +59,7 @@ public class InsetsAnimationThreadControlRunner implements InsetsAnimationContro @Override public void scheduleApplyChangeInsets(InsetsAnimationControlRunner runner) { - mControl.applyChangeInsets(mState); + mControl.applyChangeInsets(null /* outState */); } @Override diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java index 652781a310b9..5037d9e5bfe8 100644 --- a/core/java/android/view/InsetsController.java +++ b/core/java/android/view/InsetsController.java @@ -484,7 +484,9 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation /** The state dispatched from server */ private final InsetsState mLastDispatchedState = new InsetsState(); - /** The state sent to server */ + // TODO: Use other class to represent the requested visibility of each type, because the + // display frame and the frame in each source are not used. + /** The requested visibilities sent to server */ private final InsetsState mRequestedState = new InsetsState(); private final Rect mFrame = new Rect(); @@ -499,6 +501,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation private final List<WindowInsetsAnimation> mUnmodifiableTmpRunningAnims = Collections.unmodifiableList(mTmpRunningAnims); private final ArrayList<InsetsAnimationControlImpl> mTmpFinishedControls = new ArrayList<>(); + private final ArraySet<InsetsSourceConsumer> mRequestedVisibilityChanged = new ArraySet<>(); private WindowInsets mLastInsets; private boolean mAnimCallbackScheduled; @@ -640,11 +643,6 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation if (DEBUG) Log.d(TAG, "onStateChanged, notifyInsetsChanged"); mHost.notifyInsetsChanged(); } - if (!mState.equals(mLastDispatchedState, true /* excludingCaptionInsets */, - true /* excludeInvisibleIme */)) { - if (DEBUG) Log.d(TAG, "onStateChanged, send state to WM: " + mState); - updateRequestedState(); - } return true; } @@ -808,9 +806,9 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation if (hideTypes[0] != 0) { applyAnimation(hideTypes[0], false /* show */, false /* fromIme */); } - if (requestedStateStale) { - updateRequestedState(); - } + + // InsetsSourceConsumer#setControl might change the requested visibility. + updateRequestedVisibility(); } @Override @@ -945,6 +943,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation if (types == 0) { // nothing to animate. listener.onCancelled(null); + updateRequestedVisibility(); if (DEBUG) Log.d(TAG, "no types to animate in controlAnimationUnchecked"); return; } @@ -980,12 +979,14 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation } }); } + updateRequestedVisibility(); return; } if (typesReady == 0) { if (DEBUG) Log.d(TAG, "No types ready. onCancelled()"); listener.onCancelled(null); + updateRequestedVisibility(); return; } @@ -1010,6 +1011,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation } else { hideDirectly(types, false /* animationFinished */, animationType); } + updateRequestedVisibility(); } /** @@ -1177,7 +1179,6 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation } if (stateChanged) { mHost.notifyInsetsChanged(); - updateRequestedState(); } } @@ -1202,7 +1203,6 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation @VisibleForTesting public void notifyVisibilityChanged() { mHost.notifyInsetsChanged(); - updateRequestedState(); } /** @@ -1251,38 +1251,39 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation return ANIMATION_TYPE_NONE; } + @VisibleForTesting + public void onRequestedVisibilityChanged(InsetsSourceConsumer consumer) { + mRequestedVisibilityChanged.add(consumer); + } + /** - * Sends the local visibility state back to window manager if it is changed. + * Sends the requested visibilities to window manager if any of them is changed. */ - private void updateRequestedState() { + private void updateRequestedVisibility() { boolean changed = false; - for (int i = mSourceConsumers.size() - 1; i >= 0; i--) { - final InsetsSourceConsumer consumer = mSourceConsumers.valueAt(i); + for (int i = mRequestedVisibilityChanged.size() - 1; i >= 0; i--) { + final InsetsSourceConsumer consumer = mRequestedVisibilityChanged.valueAt(i); final @InternalInsetsType int type = consumer.getType(); if (type == ITYPE_CAPTION_BAR) { continue; } - if (consumer.getControl() != null) { - final InsetsSource localSource = mState.getSource(type); - if (!localSource.equals(mRequestedState.peekSource(type))) { - // Our requested state is stale. Update it here and send it to window manager. - mRequestedState.addSource(new InsetsSource(localSource)); - changed = true; - } - if (!localSource.equals(mLastDispatchedState.peekSource(type))) { - // The server state is not what we expected. This can happen while we don't have - // the control. Since we have the control now, we need to send our request again - // to modify the server state. - changed = true; - } + final boolean requestedVisible = consumer.isRequestedVisible(); + if (requestedVisible != mRequestedState.getSourceOrDefaultVisibility(type)) { + mRequestedState.getSource(type).setVisible(requestedVisible); + changed = true; } } + mRequestedVisibilityChanged.clear(); if (!changed) { return; } mHost.onInsetsModified(mRequestedState); } + InsetsState getRequestedVisibility() { + return mRequestedState; + } + @VisibleForTesting public void applyAnimation(@InsetsType final int types, boolean show, boolean fromIme) { if (types == 0) { @@ -1316,6 +1317,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation for (int i = internalTypes.size() - 1; i >= 0; i--) { getSourceConsumer(internalTypes.valueAt(i)).hide(animationFinished, animationType); } + updateRequestedVisibility(); } private void showDirectly(@InsetsType int types) { @@ -1326,6 +1328,7 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation for (int i = internalTypes.size() - 1; i >= 0; i--) { getSourceConsumer(internalTypes.valueAt(i)).show(false /* fromIme */); } + updateRequestedVisibility(); } /** diff --git a/core/java/android/view/InsetsSource.java b/core/java/android/view/InsetsSource.java index ec54007e9979..7a90bc057ccf 100644 --- a/core/java/android/view/InsetsSource.java +++ b/core/java/android/view/InsetsSource.java @@ -217,7 +217,7 @@ public class InsetsSource implements Parcelable { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { return equals(o, false); } @@ -225,7 +225,7 @@ public class InsetsSource implements Parcelable { * @param excludeInvisibleImeFrames If {@link InsetsState#ITYPE_IME} frames should be ignored * when IME is not visible. */ - public boolean equals(Object o, boolean excludeInvisibleImeFrames) { + public boolean equals(@Nullable Object o, boolean excludeInvisibleImeFrames) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; diff --git a/core/java/android/view/InsetsSourceConsumer.java b/core/java/android/view/InsetsSourceConsumer.java index d7ceaf792198..e4a24ebfe9e4 100644 --- a/core/java/android/view/InsetsSourceConsumer.java +++ b/core/java/android/view/InsetsSourceConsumer.java @@ -350,6 +350,7 @@ public class InsetsSourceConsumer { if (mRequestedVisible != requestedVisible) { mRequestedVisible = requestedVisible; mIsAnimationPending = false; + mController.onRequestedVisibilityChanged(this); if (DEBUG) Log.d(TAG, "setRequestedVisible: " + requestedVisible); } if (applyLocalVisibilityOverride()) { diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java index eabb71851303..b9f1f6a43992 100644 --- a/core/java/android/view/InsetsState.java +++ b/core/java/android/view/InsetsState.java @@ -63,8 +63,6 @@ import java.util.StringJoiner; */ public class InsetsState implements Parcelable { - public static final InsetsState EMPTY = new InsetsState(); - /** * Internal representation of inset source types. This is different from the public API in * {@link WindowInsets.Type} as one type from the public API might indicate multiple windows @@ -606,7 +604,7 @@ public class InsetsState implements Parcelable { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { return equals(o, false, false); } @@ -621,7 +619,7 @@ public class InsetsState implements Parcelable { * @return {@code true} if the two InsetsState objects are equal, {@code false} otherwise. */ @VisibleForTesting - public boolean equals(Object o, boolean excludingCaptionInsets, + public boolean equals(@Nullable Object o, boolean excludingCaptionInsets, boolean excludeInvisibleImeFrames) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } diff --git a/core/java/android/view/MagnificationSpec.java b/core/java/android/view/MagnificationSpec.java index aea337e3266e..034598a92532 100644 --- a/core/java/android/view/MagnificationSpec.java +++ b/core/java/android/view/MagnificationSpec.java @@ -16,6 +16,7 @@ package android.view; +import android.annotation.Nullable; import android.os.Parcel; import android.os.Parcelable; import android.util.Pools.SynchronizedPool; @@ -106,7 +107,7 @@ public class MagnificationSpec implements Parcelable { } @Override - public boolean equals(Object other) { + public boolean equals(@Nullable Object other) { if (this == other) { return true; } diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java index 51b0c6b59f3c..abb82bc9e16b 100644 --- a/core/java/android/view/MotionEvent.java +++ b/core/java/android/view/MotionEvent.java @@ -21,6 +21,7 @@ import static android.view.Display.DEFAULT_DISPLAY; import static java.lang.annotation.RetentionPolicy.SOURCE; import android.annotation.IntDef; +import android.annotation.Nullable; import android.annotation.TestApi; import android.compat.annotation.UnsupportedAppUsage; import android.graphics.Matrix; @@ -4160,7 +4161,7 @@ public final class MotionEvent extends InputEvent implements Parcelable { } @Override - public boolean equals(Object other) { + public boolean equals(@Nullable Object other) { if (other instanceof PointerProperties) { return equals((PointerProperties)other); } diff --git a/core/java/android/view/NotificationHeaderView.java b/core/java/android/view/NotificationHeaderView.java index 0c3d61f31dfb..546e26a85d6e 100644 --- a/core/java/android/view/NotificationHeaderView.java +++ b/core/java/android/view/NotificationHeaderView.java @@ -27,8 +27,6 @@ import android.graphics.Outline; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.util.AttributeSet; -import android.widget.ImageView; -import android.widget.LinearLayout; import android.widget.RemoteViews; import com.android.internal.R; @@ -54,7 +52,6 @@ public class NotificationHeaderView extends ViewGroup { private OnClickListener mExpandClickListener; private OnClickListener mFeedbackListener; private HeaderTouchListener mTouchListener = new HeaderTouchListener(); - private LinearLayout mTransferChip; private NotificationExpandButton mExpandButton; private CachingIconView mIcon; private View mProfileBadge; @@ -111,7 +108,6 @@ public class NotificationHeaderView extends ViewGroup { 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); - mTransferChip = findViewById(com.android.internal.R.id.media_seamless); 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); @@ -143,8 +139,7 @@ public class NotificationHeaderView extends ViewGroup { // Icons that should go at the end if ((child == mExpandButton && mShowExpandButtonAtEnd) || child == mProfileBadge - || child == mFeedbackIcon - || child == mTransferChip) { + || child == mFeedbackIcon) { iconWidth += lp.leftMargin + lp.rightMargin + child.getMeasuredWidth(); } else { totalWidth += lp.leftMargin + lp.rightMargin + child.getMeasuredWidth(); @@ -208,8 +203,7 @@ public class NotificationHeaderView extends ViewGroup { // Icons that should go at the end if ((child == mExpandButton && mShowExpandButtonAtEnd) || child == mProfileBadge - || child == mFeedbackIcon - || child == mTransferChip) { + || child == mFeedbackIcon) { if (end == getMeasuredWidth()) { layoutRight = end - mContentEndMargin; } else { @@ -349,14 +343,6 @@ public class NotificationHeaderView extends ViewGroup { } } - public View getWorkProfileIcon() { - return mProfileBadge; - } - - public CachingIconView getIcon() { - return mIcon; - } - /** * Sets the margin end for the text portion of the header, excluding right-aligned elements * @param headerTextMarginEnd margin size @@ -495,10 +481,6 @@ public class NotificationHeaderView extends ViewGroup { return this; } - public ImageView getExpandButton() { - return mExpandButton; - } - @Override public boolean hasOverlappingRendering() { return false; diff --git a/core/java/android/view/OnReceiveContentCallback.java b/core/java/android/view/OnReceiveContentCallback.java index 73bcb93d39d0..a217ff642ab7 100644 --- a/core/java/android/view/OnReceiveContentCallback.java +++ b/core/java/android/view/OnReceiveContentCallback.java @@ -134,46 +134,52 @@ public interface OnReceiveContentCallback<T extends View> { final class Payload { /** - * Specifies the UI through which content is being inserted. + * Specifies the UI through which content is being inserted. Future versions of Android may + * support additional values. * * @hide */ - @IntDef(prefix = {"SOURCE_"}, value = {SOURCE_CLIPBOARD, SOURCE_INPUT_METHOD, + @IntDef(prefix = {"SOURCE_"}, value = {SOURCE_APP, SOURCE_CLIPBOARD, SOURCE_INPUT_METHOD, SOURCE_DRAG_AND_DROP, SOURCE_AUTOFILL, SOURCE_PROCESS_TEXT}) @Retention(RetentionPolicy.SOURCE) public @interface Source {} /** + * Specifies that the operation was triggered by the app that contains the target view. + */ + public static final int SOURCE_APP = 0; + + /** * Specifies that the operation was triggered by a paste from the clipboard (e.g. "Paste" or * "Paste as plain text" action in the insertion/selection menu). */ - public static final int SOURCE_CLIPBOARD = 0; + public static final int SOURCE_CLIPBOARD = 1; /** * Specifies that the operation was triggered from the soft keyboard (also known as input * method editor or IME). See https://developer.android.com/guide/topics/text/image-keyboard * for more info. */ - public static final int SOURCE_INPUT_METHOD = 1; + public static final int SOURCE_INPUT_METHOD = 2; /** * Specifies that the operation was triggered by the drag/drop framework. See * https://developer.android.com/guide/topics/ui/drag-drop for more info. */ - public static final int SOURCE_DRAG_AND_DROP = 2; + public static final int SOURCE_DRAG_AND_DROP = 3; /** * Specifies that the operation was triggered by the autofill framework. See * https://developer.android.com/guide/topics/text/autofill for more info. */ - public static final int SOURCE_AUTOFILL = 3; + public static final int SOURCE_AUTOFILL = 4; /** * Specifies that the operation was triggered by a result from a * {@link android.content.Intent#ACTION_PROCESS_TEXT PROCESS_TEXT} action in the selection * menu. */ - public static final int SOURCE_PROCESS_TEXT = 4; + public static final int SOURCE_PROCESS_TEXT = 5; /** * Returns the symbolic name of the given source. @@ -182,6 +188,7 @@ public interface OnReceiveContentCallback<T extends View> { */ static String sourceToString(@Source int source) { switch (source) { + case SOURCE_APP: return "SOURCE_APP"; case SOURCE_CLIPBOARD: return "SOURCE_CLIPBOARD"; case SOURCE_INPUT_METHOD: return "SOURCE_INPUT_METHOD"; case SOURCE_DRAG_AND_DROP: return "SOURCE_DRAG_AND_DROP"; @@ -217,37 +224,11 @@ public interface OnReceiveContentCallback<T extends View> { return String.valueOf(flags); } - /** - * The data to be inserted. - */ @NonNull private final ClipData mClip; - - /** - * The source of the operation. See {@code SOURCE_} constants. - */ private final @Source int mSource; - - /** - * Optional flags that control the insertion behavior. See {@code FLAG_} constants. - */ private final @Flags int mFlags; - - /** - * Optional http/https URI for the content that may be provided by the IME. This is only - * populated if the source is {@link #SOURCE_INPUT_METHOD} and if a non-empty - * {@link android.view.inputmethod.InputContentInfo#getLinkUri linkUri} was passed by the - * IME. - */ - @Nullable - private final Uri mLinkUri; - - /** - * Optional additional metadata. If the source is {@link #SOURCE_INPUT_METHOD}, this will - * include the {@link android.view.inputmethod.InputConnection#commitContent opts} passed by - * the IME. - */ - @Nullable - private final Bundle mExtras; + @Nullable private final Uri mLinkUri; + @Nullable private final Bundle mExtras; private Payload(Builder b) { this.mClip = Objects.requireNonNull(b.mClip); @@ -278,7 +259,8 @@ public interface OnReceiveContentCallback<T extends View> { } /** - * The source of the operation. See {@code SOURCE_} constants. + * The source of the operation. See {@code SOURCE_} constants. Future versions of Android + * may pass additional values. */ public @Source int getSource() { return mSource; diff --git a/core/java/android/view/PointerIcon.java b/core/java/android/view/PointerIcon.java index 18d0d7b98a42..f60d98b2dc23 100644 --- a/core/java/android/view/PointerIcon.java +++ b/core/java/android/view/PointerIcon.java @@ -17,6 +17,7 @@ package android.view; import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.XmlRes; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; @@ -405,7 +406,7 @@ public final class PointerIcon implements Parcelable { } @Override - public boolean equals(Object other) { + public boolean equals(@Nullable Object other) { if (this == other) { return true; } diff --git a/core/java/android/view/RemoteAccessibilityController.java b/core/java/android/view/RemoteAccessibilityController.java new file mode 100644 index 000000000000..bc0fab1bcf8d --- /dev/null +++ b/core/java/android/view/RemoteAccessibilityController.java @@ -0,0 +1,168 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.view; + +import android.graphics.Matrix; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.RemoteException; +import android.util.Log; +import android.view.accessibility.IAccessibilityEmbeddedConnection; + +class RemoteAccessibilityController { + private static final String TAG = "RemoteAccessibilityController"; + private int mHostId; + private RemoteAccessibilityEmbeddedConnection mConnectionWrapper; + private Matrix mScreenMatrixForEmbeddedHierarchy = new Matrix(); + private final float[] mMatrixValues = new float[9]; + private View mHostView; + + RemoteAccessibilityController(View v) { + mHostView = v; + } + + private void runOnUiThread(Runnable runnable) { + final Handler h = mHostView.getHandler(); + if (h != null && h.getLooper() != Looper.myLooper()) { + h.post(runnable); + } else { + runnable.run(); + } + } + + void assosciateHierarchy(IAccessibilityEmbeddedConnection connection, + IBinder leashToken, int hostId) { + mHostId = hostId; + + try { + leashToken = connection.associateEmbeddedHierarchy( + leashToken, mHostId); + setRemoteAccessibilityEmbeddedConnection(connection, leashToken); + } catch (RemoteException e) { + Log.d(TAG, "Error in associateEmbeddedHierarchy " + e); + } + } + + void disassosciateHierarchy() { + setRemoteAccessibilityEmbeddedConnection(null, null); + } + + boolean alreadyAssociated(IAccessibilityEmbeddedConnection connection) { + if (mConnectionWrapper == null) { + return false; + } + return mConnectionWrapper.mConnection.equals(connection); + } + + boolean connected() { + return mConnectionWrapper != null; + } + + IBinder getLeashToken() { + return mConnectionWrapper.getLeashToken(); + } + + /** + * Wrapper of accessibility embedded connection for embedded view hierarchy. + */ + private final class RemoteAccessibilityEmbeddedConnection implements IBinder.DeathRecipient { + private final IAccessibilityEmbeddedConnection mConnection; + private final IBinder mLeashToken; + + RemoteAccessibilityEmbeddedConnection(IAccessibilityEmbeddedConnection connection, + IBinder leashToken) { + mConnection = connection; + mLeashToken = leashToken; + } + + IAccessibilityEmbeddedConnection getConnection() { + return mConnection; + } + + IBinder getLeashToken() { + return mLeashToken; + } + + void linkToDeath() throws RemoteException { + mConnection.asBinder().linkToDeath(this, 0); + } + + void unlinkToDeath() { + mConnection.asBinder().unlinkToDeath(this, 0); + } + + @Override + public void binderDied() { + unlinkToDeath(); + runOnUiThread(() -> { + if (mConnectionWrapper == this) { + mConnectionWrapper = null; + } + }); + } + } + + private void setRemoteAccessibilityEmbeddedConnection( + IAccessibilityEmbeddedConnection connection, IBinder leashToken) { + try { + if (mConnectionWrapper != null) { + mConnectionWrapper.getConnection() + .disassociateEmbeddedHierarchy(); + mConnectionWrapper.unlinkToDeath(); + mConnectionWrapper = null; + } + if (connection != null && leashToken != null) { + mConnectionWrapper = + new RemoteAccessibilityEmbeddedConnection(connection, leashToken); + mConnectionWrapper.linkToDeath(); + } + } catch (RemoteException e) { + Log.d(TAG, "Error while setRemoteEmbeddedConnection " + e); + } + } + + private RemoteAccessibilityEmbeddedConnection getRemoteAccessibilityEmbeddedConnection() { + return mConnectionWrapper; + } + + void setScreenMatrix(Matrix m) { + // If the screen matrix is identity or doesn't change, do nothing. + if (m.isIdentity() || m.equals(mScreenMatrixForEmbeddedHierarchy)) { + return; + } + + try { + final RemoteAccessibilityEmbeddedConnection wrapper = + getRemoteAccessibilityEmbeddedConnection(); + if (wrapper == null) { + return; + } + m.getValues(mMatrixValues); + wrapper.getConnection().setScreenMatrix(mMatrixValues); + mScreenMatrixForEmbeddedHierarchy.set(m); + } catch (RemoteException e) { + Log.d(TAG, "Error while setScreenMatrix " + e); + } + } + + + + + + +} diff --git a/core/java/android/view/ScrollCaptureCallback.java b/core/java/android/view/ScrollCaptureCallback.java index e1a4e7443600..d3aad2c72d27 100644 --- a/core/java/android/view/ScrollCaptureCallback.java +++ b/core/java/android/view/ScrollCaptureCallback.java @@ -29,8 +29,8 @@ import java.util.function.Consumer; * callbacks registered within the window. * <p> * A callback is assigned to a View using {@link View#setScrollCaptureCallback}, or to the window as - * {@link Window#addScrollCaptureCallback}. The point where the callback is registered defines the - * frame of reference for the bounds measurements used. + * {@link Window#registerScrollCaptureCallback}. The point where the callback is registered defines + * the frame of reference for the bounds measurements used. * <p> * <b>Terminology</b> * <dl> @@ -39,9 +39,9 @@ import java.util.function.Consumer; * is assigned directly to a window.</dd> * * <dt>Scroll Bounds</dt> - * <dd>A rectangle which describes an area within the containing view where scrolling content may - * be positioned. This may be the Containing View bounds itself, or any rectangle within. - * Requested by {@link #onScrollCaptureSearch}.</dd> + * <dd>A rectangle which describes an area within the containing view where scrolling content + * appears. This may be the entire view or any rectangle within. This defines a frame of reference + * for requests as well as the width and maximum height of a single request.</dd> * * <dt>Scroll Delta</dt> * <dd>The distance the scroll position has moved since capture started. Implementations are @@ -57,7 +57,7 @@ import java.util.function.Consumer; * * @see View#setScrollCaptureHint(int) * @see View#setScrollCaptureCallback(ScrollCaptureCallback) - * @see Window#addScrollCaptureCallback(ScrollCaptureCallback) + * @see Window#registerScrollCaptureCallback(ScrollCaptureCallback) * * @hide */ diff --git a/core/java/android/view/ScrollCaptureClient.java b/core/java/android/view/ScrollCaptureConnection.java index f163124f3a98..0e6cdd1dbec5 100644 --- a/core/java/android/view/ScrollCaptureClient.java +++ b/core/java/android/view/ScrollCaptureConnection.java @@ -19,7 +19,6 @@ package android.view; import static java.util.Objects.requireNonNull; import android.annotation.NonNull; -import android.annotation.Nullable; import android.annotation.UiThread; import android.annotation.WorkerThread; import android.graphics.Point; @@ -33,15 +32,15 @@ import com.android.internal.annotations.VisibleForTesting; import java.util.concurrent.atomic.AtomicBoolean; /** - * A client of the system providing Scroll Capture capability on behalf of a Window. + * Mediator between a selected scroll capture target view and a remote process. * <p> * An instance is created to wrap the selected {@link ScrollCaptureCallback}. * * @hide */ -public class ScrollCaptureClient extends IScrollCaptureClient.Stub { +public class ScrollCaptureConnection extends IScrollCaptureConnection.Stub { - private static final String TAG = "ScrollCaptureClient"; + private static final String TAG = "ScrollCaptureConnection"; private static final int DEFAULT_TIMEOUT = 1000; private final Handler mHandler; @@ -49,7 +48,7 @@ public class ScrollCaptureClient extends IScrollCaptureClient.Stub { private int mTimeoutMillis = DEFAULT_TIMEOUT; protected Surface mSurface; - private IScrollCaptureController mController; + private IScrollCaptureCallbacks mCallbacks; private final Rect mScrollBounds; private final Point mPositionInWindow; @@ -62,18 +61,18 @@ public class ScrollCaptureClient extends IScrollCaptureClient.Stub { private DelayedAction mTimeoutAction; /** - * Constructs a ScrollCaptureClient. + * Constructs a ScrollCaptureConnection. * * @param selectedTarget the target the client is controlling - * @param controller the callbacks to reply to system requests + * @param callbacks the callbacks to reply to system requests * * @hide */ - public ScrollCaptureClient( + public ScrollCaptureConnection( @NonNull ScrollCaptureTarget selectedTarget, - @NonNull IScrollCaptureController controller) { + @NonNull IScrollCaptureCallbacks callbacks) { requireNonNull(selectedTarget, "<selectedTarget> must non-null"); - requireNonNull(controller, "<controller> must non-null"); + requireNonNull(callbacks, "<callbacks> must non-null"); final Rect scrollBounds = requireNonNull(selectedTarget.getScrollBounds(), "target.getScrollBounds() must be non-null to construct a client"); @@ -82,7 +81,7 @@ public class ScrollCaptureClient extends IScrollCaptureClient.Stub { mScrollBounds = new Rect(scrollBounds); mPositionInWindow = new Point(selectedTarget.getPositionInWindow()); - mController = controller; + mCallbacks = callbacks; mCloseGuard = new CloseGuard(); mCloseGuard.open("close"); @@ -106,14 +105,13 @@ public class ScrollCaptureClient extends IScrollCaptureClient.Stub { mTimeoutMillis = timeoutMillis; } - @Nullable @VisibleForTesting public DelayedAction getTimeoutAction() { return mTimeoutAction; } private void checkConnected() { - if (mSelectedTarget == null || mController == null) { + if (mSelectedTarget == null || mCallbacks == null) { throw new IllegalStateException("This client has been disconnected."); } } @@ -124,7 +122,7 @@ public class ScrollCaptureClient extends IScrollCaptureClient.Stub { } } - @WorkerThread // IScrollCaptureClient + @WorkerThread // IScrollCaptureConnection @Override public void startCapture(Surface surface) throws RemoteException { checkConnected(); @@ -140,7 +138,7 @@ public class ScrollCaptureClient extends IScrollCaptureClient.Stub { if (cancelTimeout()) { mHandler.post(() -> { try { - mController.onCaptureStarted(); + mCallbacks.onCaptureStarted(); } catch (RemoteException e) { doShutdown(); } @@ -153,7 +151,7 @@ public class ScrollCaptureClient extends IScrollCaptureClient.Stub { endCapture(); } - @WorkerThread // IScrollCaptureClient + @WorkerThread // IScrollCaptureConnection @Override public void requestImage(Rect requestRect) { checkConnected(); @@ -170,7 +168,7 @@ public class ScrollCaptureClient extends IScrollCaptureClient.Stub { if (cancelTimeout()) { mHandler.post(() -> { try { - mController.onCaptureBufferSent(frameNumber, finalCapturedArea); + mCallbacks.onCaptureBufferSent(frameNumber, finalCapturedArea); } catch (RemoteException e) { doShutdown(); } @@ -183,7 +181,7 @@ public class ScrollCaptureClient extends IScrollCaptureClient.Stub { endCapture(); } - @WorkerThread // IScrollCaptureClient + @WorkerThread // IScrollCaptureConnection @Override public void endCapture() { if (isStarted()) { @@ -196,7 +194,7 @@ public class ScrollCaptureClient extends IScrollCaptureClient.Stub { } private boolean isStarted() { - return mController != null && mSelectedTarget != null; + return mCallbacks != null && mSelectedTarget != null; } @UiThread @@ -214,8 +212,8 @@ public class ScrollCaptureClient extends IScrollCaptureClient.Stub { private void doShutdown() { try { - if (mController != null) { - mController.onConnectionClosed(); + if (mCallbacks != null) { + mCallbacks.onConnectionClosed(); } } catch (RemoteException e) { // Ignore @@ -235,15 +233,15 @@ public class ScrollCaptureClient extends IScrollCaptureClient.Stub { } mSelectedTarget = null; - mController = null; + mCallbacks = null; } /** @return a string representation of the state of this client */ public String toString() { - return "ScrollCaptureClient{" + return "ScrollCaptureConnection{" + ", session=" + mSession + ", selectedTarget=" + mSelectedTarget - + ", clientCallbacks=" + mController + + ", clientCallbacks=" + mCallbacks + "}"; } diff --git a/core/java/android/view/ScrollCaptureSession.java b/core/java/android/view/ScrollCaptureSession.java index 628e23fb3f5e..92617a325265 100644 --- a/core/java/android/view/ScrollCaptureSession.java +++ b/core/java/android/view/ScrollCaptureSession.java @@ -36,15 +36,15 @@ public class ScrollCaptureSession { private final Point mPositionInWindow; @Nullable - private ScrollCaptureClient mClient; + private ScrollCaptureConnection mConnection; /** @hide */ public ScrollCaptureSession(Surface surface, Rect scrollBounds, Point positionInWindow, - ScrollCaptureClient client) { + ScrollCaptureConnection connection) { mSurface = surface; mScrollBounds = scrollBounds; mPositionInWindow = positionInWindow; - mClient = client; + mConnection = connection; } /** @@ -88,8 +88,8 @@ public class ScrollCaptureSession { * @param capturedArea the area captured, relative to scroll bounds */ public void notifyBufferSent(long frameNumber, @NonNull Rect capturedArea) { - if (mClient != null) { - mClient.onRequestImageCompleted(frameNumber, capturedArea); + if (mConnection != null) { + mConnection.onRequestImageCompleted(frameNumber, capturedArea); } } @@ -97,7 +97,7 @@ public class ScrollCaptureSession { * @hide */ public void disconnect() { - mClient = null; + mConnection = null; if (mSurface.isValid()) { mSurface.release(); } diff --git a/core/java/android/view/ScrollCaptureTargetResolver.java b/core/java/android/view/ScrollCaptureTargetResolver.java index 71e82c511e2c..5106534694a1 100644 --- a/core/java/android/view/ScrollCaptureTargetResolver.java +++ b/core/java/android/view/ScrollCaptureTargetResolver.java @@ -57,7 +57,6 @@ import java.util.function.Consumer; @UiThread public class ScrollCaptureTargetResolver { private static final String TAG = "ScrollCaptureTargetRes"; - private static final boolean DEBUG = true; private final Object mLock = new Object(); @@ -86,18 +85,11 @@ public class ScrollCaptureTargetResolver { * Binary operator which selects the best {@link ScrollCaptureTarget}. */ private static ScrollCaptureTarget chooseTarget(ScrollCaptureTarget a, ScrollCaptureTarget b) { - Log.d(TAG, "chooseTarget: " + a + " or " + b); - // Nothing plus nothing is still nothing. if (a == null && b == null) { - Log.d(TAG, "chooseTarget: (both null) return " + null); return null; - } - // Prefer non-null. - if (a == null || b == null) { + } else if (a == null || b == null) { ScrollCaptureTarget c = (a == null) ? b : a; - Log.d(TAG, "chooseTarget: (other is null) return " + c); return c; - } boolean emptyScrollBoundsA = nullOrEmpty(a.getScrollBounds()); @@ -155,8 +147,7 @@ public class ScrollCaptureTargetResolver { * * @param targets a list of {@link ScrollCaptureTarget} as collected by {@link * View#dispatchScrollCaptureSearch}. - * @param uiHandler the UI thread handler for the view tree - * @see #start(long, Consumer) + * @see #start(Handler, long, Consumer) */ public ScrollCaptureTargetResolver(Queue<ScrollCaptureTarget> targets) { mTargets = targets; @@ -184,7 +175,6 @@ public class ScrollCaptureTargetResolver { return mResult; } - private void supplyResult(ScrollCaptureTarget target) { checkThread(); if (mFinished) { @@ -232,12 +222,11 @@ public class ScrollCaptureTargetResolver { return; } mStarted = true; - uiHandler.post(() -> run(timeLimitMillis, resultConsumer)); + uiHandler.post(this::run); } } - - private void run(long timeLimitMillis, Consumer<ScrollCaptureTarget> resultConsumer) { + private void run() { checkThread(); mPendingBoundsRequests = mTargets.size(); @@ -248,15 +237,11 @@ public class ScrollCaptureTargetResolver { mHandler.postAtTime(mTimeoutRunnable, mDeadlineMillis); } - private final Runnable mTimeoutRunnable = new Runnable() { - @Override - public void run() { - checkThread(); - supplyResult(null); - } + private final Runnable mTimeoutRunnable = () -> { + checkThread(); + supplyResult(null); }; - /** * Adds a target to the list and requests {@link ScrollCaptureCallback#onScrollCaptureSearch} * scrollBounds} from it. Results are returned by a call to {@link #onScrollBoundsProvided}. @@ -274,7 +259,6 @@ public class ScrollCaptureTargetResolver { // Queue and consume on the UI thread ((scrollBounds) -> mHandler.post( () -> onScrollBoundsProvided(target, scrollBounds))))); - } @UiThread @@ -300,14 +284,8 @@ public class ScrollCaptureTargetResolver { supplyResult(target); } - System.err.println("mPendingBoundsRequests: " + mPendingBoundsRequests); - System.err.println("mDeadlineMillis: " + mDeadlineMillis); - System.err.println("SystemClock.elapsedRealtime(): " + SystemClock.elapsedRealtime()); - if (!mFinished) { // Reschedule the timeout. - System.err.println( - "We think we're NOT done yet and will check back at " + mDeadlineMillis); mHandler.postAtTime(mTimeoutRunnable, mDeadlineMillis); } } @@ -334,44 +312,17 @@ public class ScrollCaptureTargetResolver { return otherParent == view; } - private static int findRelation(@NonNull View a, @NonNull View b) { - if (a == b) { - return 0; - } - - ViewParent parentA = a.getParent(); - ViewParent parentB = b.getParent(); - - while (parentA != null || parentB != null) { - if (parentA == parentB) { - return 0; - } - if (parentA == b) { - return 1; // A is descendant of B - } - if (parentB == a) { - return -1; // B is descendant of A - } - if (parentA != null) { - parentA = parentA.getParent(); - } - if (parentB != null) { - parentB = parentB.getParent(); - } - } - return 0; - } - /** * A safe wrapper for a consumer callbacks intended to accept a single value. It ensures * that the receiver of the consumer does not retain a reference to {@code target} after use nor * cause race conditions by invoking {@link Consumer#accept accept} more than once. - * - * @param target the target consumer */ static class SingletonConsumer<T> implements Consumer<T> { final AtomicReference<Consumer<T>> mAtomicRef; + /** + * @param target the target consumer + **/ SingletonConsumer(Consumer<T> target) { mAtomicRef = new AtomicReference<>(target); } diff --git a/core/java/android/view/Surface.java b/core/java/android/view/Surface.java index 5b0d950e3bd8..0847a179c553 100644 --- a/core/java/android/view/Surface.java +++ b/core/java/android/view/Surface.java @@ -739,7 +739,7 @@ public class Surface implements Parcelable { * Set the scaling mode to be used for this surfaces buffers * @hide */ - void setScalingMode(@ScalingMode int scalingMode) { + public void setScalingMode(@ScalingMode int scalingMode) { synchronized (mLock) { checkNotReleasedLocked(); int err = nativeSetScalingMode(mNativeObject, scalingMode); diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java index ed9deecd7e88..6d6fabb16b72 100644 --- a/core/java/android/view/SurfaceControl.java +++ b/core/java/android/view/SurfaceControl.java @@ -187,8 +187,6 @@ public final class SurfaceControl implements Parcelable { private static native void nativeReparent(long transactionObj, long nativeObject, long newParentNativeObject); private static native void nativeSeverChildren(long transactionObj, long nativeObject); - private static native void nativeSetOverrideScalingMode(long transactionObj, long nativeObject, - int scalingMode); private static native Display.HdrCapabilities nativeGetHdrCapabilities(IBinder displayToken); @@ -1521,16 +1519,6 @@ public final class SurfaceControl implements Parcelable { /** * @hide */ - public void setOverrideScalingMode(int scalingMode) { - checkNotReleased(); - synchronized(SurfaceControl.class) { - sGlobalTransaction.setOverrideScalingMode(this, scalingMode); - } - } - - /** - * @hide - */ @UnsupportedAppUsage public void setLayer(int zorder) { checkNotReleased(); @@ -1635,6 +1623,16 @@ public final class SurfaceControl implements Parcelable { /** * @hide */ + public void setBackgroundBlurRadius(int blur) { + checkNotReleased(); + synchronized (SurfaceControl.class) { + sGlobalTransaction.setBackgroundBlurRadius(this, blur); + } + } + + /** + * @hide + */ public void setMatrix(float dsdx, float dtdx, float dtdy, float dsdy) { checkNotReleased(); synchronized(SurfaceControl.class) { @@ -1748,7 +1746,7 @@ public final class SurfaceControl implements Parcelable { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; DisplayInfo that = (DisplayInfo) o; @@ -1926,7 +1924,7 @@ public final class SurfaceControl implements Parcelable { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { return o instanceof DesiredDisplayConfigSpecs && equals((DesiredDisplayConfigSpecs) o); } @@ -2989,16 +2987,6 @@ public final class SurfaceControl implements Parcelable { } /** - * @hide - */ - public Transaction setOverrideScalingMode(SurfaceControl sc, int overrideScalingMode) { - checkPreconditions(sc); - nativeSetOverrideScalingMode(mNativeObject, sc.mNativeObject, - overrideScalingMode); - return this; - } - - /** * Fills the surface with the specified color. * @param color A float array with three values to represent r, g, b in range [0..1]. An * invalid color will remove the color fill. diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java index 78c71b856e2c..432d9279c48d 100644 --- a/core/java/android/view/SurfaceView.java +++ b/core/java/android/view/SurfaceView.java @@ -41,7 +41,6 @@ import android.os.Build; import android.os.Handler; import android.os.IBinder; import android.os.Looper; -import android.os.RemoteException; import android.os.SystemClock; import android.provider.Settings; import android.util.AttributeSet; @@ -225,13 +224,12 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall private SurfaceControl.Transaction mRtTransaction = new SurfaceControl.Transaction(); private SurfaceControl.Transaction mTmpTransaction = new SurfaceControl.Transaction(); - private int mParentSurfaceGenerationId; + private int mParentSurfaceSequenceId; - private RemoteAccessibilityEmbeddedConnection mRemoteAccessibilityEmbeddedConnection; + private RemoteAccessibilityController mRemoteAccessibilityController = + new RemoteAccessibilityController(this); - private final Matrix mScreenMatrixForEmbeddedHierarchy = new Matrix(); private final Matrix mTmpMatrix = new Matrix(); - private final float[] mMatrixValues = new float[9]; SurfaceControlViewHost.SurfacePackage mSurfacePackage; @@ -467,7 +465,7 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall Transaction t = new SurfaceControl.Transaction(); t.setAlpha(mSurfaceControl, alpha); t.deferTransactionUntil(mSurfaceControl, - viewRoot.getRenderSurfaceControl(), frame); + viewRoot.getSurfaceControl(), frame); t.apply(); } } @@ -827,7 +825,7 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall final SurfaceControl.Transaction t = new SurfaceControl.Transaction(); updateRelativeZ(t); t.deferTransactionUntil(mSurfaceControl, - viewRoot.getRenderSurfaceControl(), frame); + viewRoot.getSurfaceControl(), frame); t.apply(); } } @@ -927,6 +925,103 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall } } + private boolean performSurfaceTransaction(ViewRootImpl viewRoot, Translator translator, + boolean creating, boolean sizeChanged, boolean needBLASTSync) { + boolean realSizeChanged = false; + + mSurfaceLock.lock(); + try { + mDrawingStopped = !mVisible; + + if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " " + + "Cur surface: " + mSurface); + + // If we are creating the surface control or the parent surface has not + // changed, then set relative z. Otherwise allow the parent + // SurfaceChangedCallback to update the relative z. This is needed so that + // we do not change the relative z before the server is ready to swap the + // parent surface. + if (creating || (mParentSurfaceSequenceId == viewRoot.getSurfaceSequenceId())) { + updateRelativeZ(mTmpTransaction); + } + mParentSurfaceSequenceId = viewRoot.getSurfaceSequenceId(); + + if (mViewVisibility) { + mTmpTransaction.show(mSurfaceControl); + } else { + mTmpTransaction.hide(mSurfaceControl); + } + + if (mSurfacePackage != null) { + reparentSurfacePackage(mTmpTransaction, mSurfacePackage); + } + + updateBackgroundVisibility(mTmpTransaction); + updateBackgroundColor(mTmpTransaction); + if (mUseAlpha) { + float alpha = getFixedAlpha(); + mTmpTransaction.setAlpha(mSurfaceControl, alpha); + mSurfaceAlpha = alpha; + } + + // While creating the surface, we will set it's initial + // geometry. Outside of that though, we should generally + // leave it to the RenderThread. + // + // There is one more case when the buffer size changes we aren't yet + // prepared to sync (as even following the transaction applying + // we still need to latch a buffer). + // b/28866173 + if (sizeChanged || creating || !mRtHandlingPositionUpdates) { + onSetSurfacePositionAndScaleRT(mTmpTransaction, mSurfaceControl, + mScreenRect.left, /*positionLeft*/ + mScreenRect.top /*positionTop*/ , + mScreenRect.width() / (float) mSurfaceWidth /*postScaleX*/, + mScreenRect.height() / (float) mSurfaceHeight /*postScaleY*/); + + // Set a window crop when creating the surface or changing its size to + // crop the buffer to the surface size since the buffer producer may + // use SCALING_MODE_SCALE and submit a larger size than the surface + // size. + if (mClipSurfaceToBounds && mClipBounds != null) { + mTmpTransaction.setWindowCrop(mSurfaceControl, mClipBounds); + } else { + mTmpTransaction.setWindowCrop(mSurfaceControl, mSurfaceWidth, + mSurfaceHeight); + } + } else if (needBLASTSync) { + viewRoot.setUseBLASTSyncTransaction(); + } + mTmpTransaction.setCornerRadius(mSurfaceControl, mCornerRadius); + if (sizeChanged && !creating) { + setBufferSize(mTmpTransaction); + } + + mTmpTransaction.apply(); + updateEmbeddedAccessibilityMatrix(); + + mSurfaceFrame.left = 0; + mSurfaceFrame.top = 0; + if (translator == null) { + mSurfaceFrame.right = mSurfaceWidth; + mSurfaceFrame.bottom = mSurfaceHeight; + } else { + float appInvertedScale = translator.applicationInvertedScale; + mSurfaceFrame.right = (int) (mSurfaceWidth * appInvertedScale + 0.5f); + mSurfaceFrame.bottom = (int) (mSurfaceHeight * appInvertedScale + 0.5f); + } + final int surfaceWidth = mSurfaceFrame.right; + final int surfaceHeight = mSurfaceFrame.bottom; + realSizeChanged = mLastSurfaceWidth != surfaceWidth + || mLastSurfaceHeight != surfaceHeight; + mLastSurfaceWidth = surfaceWidth; + mLastSurfaceHeight = surfaceHeight; + } finally { + mSurfaceLock.unlock(); + } + return realSizeChanged; + } + /** @hide */ protected void updateSurface() { if (!mHaveFrame) { @@ -965,7 +1060,6 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall && mRequestedVisible; final boolean sizeChanged = mSurfaceWidth != myWidth || mSurfaceHeight != myHeight; final boolean windowVisibleChanged = mWindowVisibility != mLastWindowVisibility; - boolean redrawNeeded = false; getLocationInSurface(mLocation); final boolean positionChanged = mWindowSpaceLeft != mLocation[0] || mWindowSpaceTop != mLocation[1]; @@ -988,7 +1082,7 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall + " top=" + (mWindowSpaceTop != mLocation[1])); try { - final boolean visible = mVisible = mRequestedVisible; + mVisible = mRequestedVisible; mWindowSpaceLeft = mLocation[0]; mWindowSpaceTop = mLocation[1]; mSurfaceWidth = myWidth; @@ -1014,119 +1108,26 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall return; } - boolean realSizeChanged = false; + final boolean needBLASTSync = + (layoutSizeChanged || positionChanged || visibleChanged) && + viewRoot.useBLAST(); + final boolean realSizeChanged = performSurfaceTransaction(viewRoot, + translator, creating, sizeChanged, needBLASTSync); + final boolean redrawNeeded = sizeChanged || creating || + (mVisible && !mDrawFinished); - mSurfaceLock.lock(); try { - mDrawingStopped = !visible; - - if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " " - + "Cur surface: " + mSurface); - - // If we are creating the surface control or the parent surface has not - // changed, then set relative z. Otherwise allow the parent - // SurfaceChangedCallback to update the relative z. This is needed so that - // we do not change the relative z before the server is ready to swap the - // parent surface. - if (creating || (mParentSurfaceGenerationId - == viewRoot.mSurface.getGenerationId())) { - updateRelativeZ(mTmpTransaction); - } - mParentSurfaceGenerationId = viewRoot.mSurface.getGenerationId(); - - if (mViewVisibility) { - mTmpTransaction.show(mSurfaceControl); - } else { - mTmpTransaction.hide(mSurfaceControl); - } - - if (mSurfacePackage != null) { - reparentSurfacePackage(mTmpTransaction, mSurfacePackage); - } - - updateBackgroundVisibility(mTmpTransaction); - updateBackgroundColor(mTmpTransaction); - if (mUseAlpha) { - mTmpTransaction.setAlpha(mSurfaceControl, alpha); - mSurfaceAlpha = alpha; - } - - // While creating the surface, we will set it's initial - // geometry. Outside of that though, we should generally - // leave it to the RenderThread. - // - // There is one more case when the buffer size changes we aren't yet - // prepared to sync (as even following the transaction applying - // we still need to latch a buffer). - // b/28866173 - if (sizeChanged || creating || !mRtHandlingPositionUpdates) { - onSetSurfacePositionAndScaleRT(mTmpTransaction, mSurfaceControl, - mScreenRect.left, /*positionLeft*/ - mScreenRect.top /*positionTop*/ , - mScreenRect.width() / (float) mSurfaceWidth /*postScaleX*/, - mScreenRect.height() / (float) mSurfaceHeight /*postScaleY*/); - - // Set a window crop when creating the surface or changing its size to - // crop the buffer to the surface size since the buffer producer may - // use SCALING_MODE_SCALE and submit a larger size than the surface - // size. - if (mClipSurfaceToBounds && mClipBounds != null) { - mTmpTransaction.setWindowCrop(mSurfaceControl, mClipBounds); - } else { - mTmpTransaction.setWindowCrop(mSurfaceControl, mSurfaceWidth, - mSurfaceHeight); - } - } else if ((layoutSizeChanged || positionChanged || visibleChanged) && - viewRoot.useBLAST()) { - viewRoot.setUseBLASTSyncTransaction(); - } - mTmpTransaction.setCornerRadius(mSurfaceControl, mCornerRadius); - if (sizeChanged && !creating) { - setBufferSize(mTmpTransaction); - } - - mTmpTransaction.apply(); - updateScreenMatrixForEmbeddedHierarchy(); - - if (sizeChanged || creating) { - redrawNeeded = true; - } - - mSurfaceFrame.left = 0; - mSurfaceFrame.top = 0; - if (translator == null) { - mSurfaceFrame.right = mSurfaceWidth; - mSurfaceFrame.bottom = mSurfaceHeight; - } else { - float appInvertedScale = translator.applicationInvertedScale; - mSurfaceFrame.right = (int) (mSurfaceWidth * appInvertedScale + 0.5f); - mSurfaceFrame.bottom = (int) (mSurfaceHeight * appInvertedScale + 0.5f); - } - - final int surfaceWidth = mSurfaceFrame.right; - final int surfaceHeight = mSurfaceFrame.bottom; - realSizeChanged = mLastSurfaceWidth != surfaceWidth - || mLastSurfaceHeight != surfaceHeight; - mLastSurfaceWidth = surfaceWidth; - mLastSurfaceHeight = surfaceHeight; - } finally { - mSurfaceLock.unlock(); - } - - try { - redrawNeeded |= visible && !mDrawFinished; - SurfaceHolder.Callback[] callbacks = null; final boolean surfaceChanged = creating; - if (mSurfaceCreated && (surfaceChanged || (!visible && visibleChanged))) { + if (mSurfaceCreated && (surfaceChanged || (!mVisible && visibleChanged))) { mSurfaceCreated = false; notifySurfaceDestroyed(); } copySurface(creating /* surfaceControlCreated */, sizeChanged); - if (visible && mSurface.isValid()) { + if (mVisible && mSurface.isValid()) { if (!mSurfaceCreated && (surfaceChanged || visibleChanged)) { mSurfaceCreated = true; mIsCreating = true; @@ -1352,7 +1353,7 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall Rect position, long frameNumber) { final ViewRootImpl viewRoot = getViewRootImpl(); if (frameNumber > 0 && viewRoot != null && !viewRoot.useBLAST()) { - t.deferTransactionUntil(surface, viewRoot.getRenderSurfaceControl(), + t.deferTransactionUntil(surface, viewRoot.getSurfaceControl(), frameNumber); } @@ -1470,7 +1471,7 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall } else { if (frameNumber > 0 && viewRoot != null && viewRoot.mSurface.isValid()) { mRtTransaction.deferTransactionUntil(mSurfaceControl, - viewRoot.getRenderSurfaceControl(), frameNumber); + viewRoot.getSurfaceControl(), frameNumber); } mRtTransaction.hide(mSurfaceControl); if (mRtReleaseSurfaces) { @@ -1754,7 +1755,7 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall @Override public void surfaceDestroyed() { setWindowStopped(true); - setRemoteAccessibilityEmbeddedConnection(null, null); + mRemoteAccessibilityController.disassosciateHierarchy(); } /** @@ -1834,14 +1835,12 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall @Override public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) { super.onInitializeAccessibilityNodeInfoInternal(info); - final RemoteAccessibilityEmbeddedConnection wrapper = - getRemoteAccessibilityEmbeddedConnection(); - if (wrapper == null) { + if (!mRemoteAccessibilityController.connected()) { return; } // Add a leashed child when this SurfaceView embeds another view hierarchy. Getting this // leashed child would return the root node in the embedded hierarchy - info.addChild(wrapper.getLeashToken()); + info.addChild(mRemoteAccessibilityController.getLeashToken()); } @Override @@ -1850,7 +1849,7 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall // If developers explicitly set the important mode for it, don't change the mode. // Only change the mode to important when this SurfaceView isn't explicitly set and has // an embedded hierarchy. - if (mRemoteAccessibilityEmbeddedConnection == null + if ((mRemoteAccessibilityController!= null && !mRemoteAccessibilityController.connected()) || mode != IMPORTANT_FOR_ACCESSIBILITY_AUTO) { return mode; } @@ -1859,74 +1858,13 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall private void initEmbeddedHierarchyForAccessibility(SurfaceControlViewHost.SurfacePackage p) { final IAccessibilityEmbeddedConnection connection = p.getAccessibilityEmbeddedConnection(); - final RemoteAccessibilityEmbeddedConnection wrapper = - getRemoteAccessibilityEmbeddedConnection(); - - // Do nothing if package is embedding the same view hierarchy. - if (wrapper != null && wrapper.getConnection().equals(connection)) { - return; - } - - // If this SurfaceView embeds a different view hierarchy, unlink the previous one first. - setRemoteAccessibilityEmbeddedConnection(null, null); - - try { - final IBinder leashToken = connection.associateEmbeddedHierarchy( - getViewRootImpl().mLeashToken, getAccessibilityViewId()); - setRemoteAccessibilityEmbeddedConnection(connection, leashToken); - } catch (RemoteException e) { - Log.d(TAG, "Error while associateEmbeddedHierarchy " + e); - } - updateScreenMatrixForEmbeddedHierarchy(); - } - - private void setRemoteAccessibilityEmbeddedConnection( - IAccessibilityEmbeddedConnection connection, IBinder leashToken) { - try { - if (mRemoteAccessibilityEmbeddedConnection != null) { - mRemoteAccessibilityEmbeddedConnection.getConnection() - .disassociateEmbeddedHierarchy(); - mRemoteAccessibilityEmbeddedConnection.unlinkToDeath(); - mRemoteAccessibilityEmbeddedConnection = null; - } - if (connection != null && leashToken != null) { - mRemoteAccessibilityEmbeddedConnection = - new RemoteAccessibilityEmbeddedConnection(connection, leashToken); - mRemoteAccessibilityEmbeddedConnection.linkToDeath(); - } - } catch (RemoteException e) { - Log.d(TAG, "Error while setRemoteEmbeddedConnection " + e); - } - } - - private RemoteAccessibilityEmbeddedConnection getRemoteAccessibilityEmbeddedConnection() { - return mRemoteAccessibilityEmbeddedConnection; - } - - private void updateScreenMatrixForEmbeddedHierarchy() { - getBoundsOnScreen(mTmpRect); - mTmpMatrix.reset(); - mTmpMatrix.setTranslate(mTmpRect.left, mTmpRect.top); - mTmpMatrix.postScale(mScreenRect.width() / (float) mSurfaceWidth, - mScreenRect.height() / (float) mSurfaceHeight); - - // If the screen matrix is identity or doesn't change, do nothing. - if (mTmpMatrix.isIdentity() || mTmpMatrix.equals(mScreenMatrixForEmbeddedHierarchy)) { + if (mRemoteAccessibilityController.alreadyAssociated(connection)) { return; } + mRemoteAccessibilityController.assosciateHierarchy(connection, + getViewRootImpl().mLeashToken, getAccessibilityViewId()); - try { - final RemoteAccessibilityEmbeddedConnection wrapper = - getRemoteAccessibilityEmbeddedConnection(); - if (wrapper == null) { - return; - } - mTmpMatrix.getValues(mMatrixValues); - wrapper.getConnection().setScreenMatrix(mMatrixValues); - mScreenMatrixForEmbeddedHierarchy.set(mTmpMatrix); - } catch (RemoteException e) { - Log.d(TAG, "Error while setScreenMatrix " + e); - } + updateEmbeddedAccessibilityMatrix(); } private void notifySurfaceDestroyed() { @@ -1954,6 +1892,18 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall } } + void updateEmbeddedAccessibilityMatrix() { + if (!mRemoteAccessibilityController.connected()) { + return; + } + getBoundsOnScreen(mTmpRect); + mTmpMatrix.reset(); + mTmpMatrix.setTranslate(mTmpRect.left, mTmpRect.top); + mTmpMatrix.postScale(mScreenRect.width() / (float) mSurfaceWidth, + mScreenRect.height() / (float) mSurfaceHeight); + mRemoteAccessibilityController.setScreenMatrix(mTmpMatrix); + } + @Override protected void onFocusChanged(boolean gainFocus, @FocusDirection int direction, @Nullable Rect previouslyFocusedRect) { @@ -1970,44 +1920,4 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall + "Exception requesting focus on embedded window", e); } } - - /** - * Wrapper of accessibility embedded connection for embedded view hierarchy. - */ - private final class RemoteAccessibilityEmbeddedConnection implements IBinder.DeathRecipient { - private final IAccessibilityEmbeddedConnection mConnection; - private final IBinder mLeashToken; - - RemoteAccessibilityEmbeddedConnection(IAccessibilityEmbeddedConnection connection, - IBinder leashToken) { - mConnection = connection; - mLeashToken = leashToken; - } - - IAccessibilityEmbeddedConnection getConnection() { - return mConnection; - } - - IBinder getLeashToken() { - return mLeashToken; - } - - void linkToDeath() throws RemoteException { - mConnection.asBinder().linkToDeath(this, 0); - } - - void unlinkToDeath() { - mConnection.asBinder().unlinkToDeath(this, 0); - } - - @Override - public void binderDied() { - unlinkToDeath(); - runOnUiThread(() -> { - if (mRemoteAccessibilityEmbeddedConnection == this) { - mRemoteAccessibilityEmbeddedConnection = null; - } - }); - } - } } diff --git a/core/java/android/view/SyncRtSurfaceTransactionApplier.java b/core/java/android/view/SyncRtSurfaceTransactionApplier.java index 062285ff2f5d..bce78b572360 100644 --- a/core/java/android/view/SyncRtSurfaceTransactionApplier.java +++ b/core/java/android/view/SyncRtSurfaceTransactionApplier.java @@ -60,7 +60,7 @@ public class SyncRtSurfaceTransactionApplier { if (mTargetViewRootImpl == null) { return; } - mTargetSc = mTargetViewRootImpl.getRenderSurfaceControl(); + mTargetSc = mTargetViewRootImpl.getSurfaceControl(); mTargetViewRootImpl.registerRtFrameCallback(frame -> { if (mTargetSc == null || !mTargetSc.isValid()) { return; diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java index 14a324dc3abe..57ca71ae4b02 100644 --- a/core/java/android/view/ThreadedRenderer.java +++ b/core/java/android/view/ThreadedRenderer.java @@ -18,6 +18,7 @@ package android.view; import android.annotation.NonNull; import android.annotation.Nullable; +import android.app.ActivityManager; import android.content.Context; import android.content.res.TypedArray; import android.graphics.HardwareRenderer; @@ -26,7 +27,6 @@ import android.graphics.Point; import android.graphics.RecordingCanvas; import android.graphics.Rect; import android.graphics.RenderNode; -import android.os.SystemProperties; import android.os.Trace; import android.util.Log; import android.view.Surface.OutOfResourcesException; @@ -186,37 +186,12 @@ public final class ThreadedRenderer extends HardwareRenderer { public static int EGL_CONTEXT_PRIORITY_MEDIUM_IMG = 0x3102; public static int EGL_CONTEXT_PRIORITY_LOW_IMG = 0x3103; - static { - // Try to check OpenGL support early if possible. - isAvailable(); - } - - /** - * A process can set this flag to false to prevent the use of threaded - * rendering. - * - * @hide - */ - public static boolean sRendererDisabled = false; - /** * Further threaded renderer disabling for the system process. * * @hide */ - public static boolean sSystemRendererDisabled = false; - - /** - * Invoke this method to disable threaded rendering in the current process. - * - * @hide - */ - public static void disable(boolean system) { - sRendererDisabled = true; - if (system) { - sSystemRendererDisabled = true; - } - } + public static boolean sRendererEnabled = true; public static boolean sTrimForeground = false; @@ -230,16 +205,19 @@ public final class ThreadedRenderer extends HardwareRenderer { sTrimForeground = true; } - /** - * Indicates whether threaded rendering is available under any form for - * the view hierarchy. - * - * @return True if the view hierarchy can potentially be defer rendered, - * false otherwise + * Initialize HWUI for being in a system process like system_server + * Should not be called in non-system processes */ - public static boolean isAvailable() { - return true; + public static void initForSystemProcess() { + // The system process on low-memory devices do not get to use hardware + // accelerated drawing, since this can add too much overhead to the + // process. + if (!ActivityManager.isHighEndGfx()) { + sRendererEnabled = false; + } else { + enableForegroundTrimming(); + } } /** @@ -250,11 +228,7 @@ public final class ThreadedRenderer extends HardwareRenderer { * @return A threaded renderer backed by OpenGL. */ public static ThreadedRenderer create(Context context, boolean translucent, String name) { - ThreadedRenderer renderer = null; - if (isAvailable()) { - renderer = new ThreadedRenderer(context, translucent, name); - } - return renderer; + return new ThreadedRenderer(context, translucent, name); } private static final String[] VISUALIZERS = { diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 4f05a599c171..cf5ca56eb188 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -23573,8 +23573,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, if ((privateFlags & PFLAG_SELECTED) != 0) viewStateIndex |= StateSet.VIEW_STATE_SELECTED; if (hasWindowFocus()) viewStateIndex |= StateSet.VIEW_STATE_WINDOW_FOCUSED; if ((privateFlags & PFLAG_ACTIVATED) != 0) viewStateIndex |= StateSet.VIEW_STATE_ACTIVATED; - if (mAttachInfo != null && mAttachInfo.mHardwareAccelerationRequested && - ThreadedRenderer.isAvailable()) { + if (mAttachInfo != null && mAttachInfo.mHardwareAccelerationRequested) { // This is set if HW acceleration is requested, even if the current // process doesn't allow it. This is just to allow app preview // windows to better match their app. diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index e00ff7e00888..f0203011b4f2 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -105,6 +105,7 @@ import android.graphics.BLASTBufferQueue; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.FrameInfo; +import android.graphics.HardwareRenderer; import android.graphics.HardwareRenderer.FrameDrawingCallback; import android.graphics.Insets; import android.graphics.Matrix; @@ -519,7 +520,6 @@ public final class ViewRootImpl implements ViewParent, @UnsupportedAppUsage public final Surface mSurface = new Surface(); private final SurfaceControl mSurfaceControl = new SurfaceControl(); - private SurfaceControl mBlastSurfaceControl = new SurfaceControl(); private BLASTBufferQueue mBlastBufferQueue; @@ -649,7 +649,7 @@ public final class ViewRootImpl implements ViewParent, private final InsetsController mInsetsController; private final ImeFocusController mImeFocusController; - private ScrollCaptureClient mScrollCaptureClient; + private ScrollCaptureConnection mScrollCaptureConnection; /** * @return {@link ImeFocusController} for this instance. @@ -659,10 +659,10 @@ public final class ViewRootImpl implements ViewParent, return mImeFocusController; } - /** @return The current {@link ScrollCaptureClient} for this instance, if any is active. */ + /** @return The current {@link ScrollCaptureConnection} for this instance, if any is active. */ @Nullable - public ScrollCaptureClient getScrollCaptureClient() { - return mScrollCaptureClient; + public ScrollCaptureConnection getScrollCaptureConnection() { + return mScrollCaptureConnection; } private final GestureExclusionTracker mGestureExclusionTracker = new GestureExclusionTracker(); @@ -702,6 +702,11 @@ public final class ViewRootImpl implements ViewParent, private HashSet<ScrollCaptureCallback> mRootScrollCaptureCallbacks; + /** + * Increment this value when the surface has been replaced. + */ + private int mSurfaceSequenceId = 0; + private String mTag = TAG; public ViewRootImpl(Context context, Display display) { @@ -1016,8 +1021,10 @@ public final class ViewRootImpl implements ViewParent, mAttachInfo.mRecomputeGlobalAttributes = true; collectViewAttributes(); adjustLayoutParamsForCompatibility(mWindowAttributes); + controlInsetsForCompatibility(mWindowAttributes); res = mWindowSession.addToDisplayAsUser(mWindow, mWindowAttributes, - getHostVisibility(), mDisplay.getDisplayId(), userId, mTmpFrames.frame, + getHostVisibility(), mDisplay.getDisplayId(), userId, + mInsetsController.getRequestedVisibility(), mTmpFrames.frame, mAttachInfo.mContentInsets, mAttachInfo.mStableInsets, mAttachInfo.mDisplayCutout, inputChannel, mTempInsets, mTempControls); @@ -1286,10 +1293,6 @@ public final class ViewRootImpl implements ViewParent, (attrs.flags & WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED) != 0; if (hardwareAccelerated) { - if (!ThreadedRenderer.isAvailable()) { - return; - } - // Persistent processes (including the system) should not do // accelerated rendering on low-end devices. In that case, // sRendererDisabled will be set. In addition, the system process @@ -1309,8 +1312,7 @@ public final class ViewRootImpl implements ViewParent, // shows for launching applications, so they will look more like // the app being launched. mAttachInfo.mHardwareAccelerationRequested = true; - } else if (!ThreadedRenderer.sRendererDisabled - || (ThreadedRenderer.sSystemRendererDisabled && forceHwAccelerated)) { + } else if (ThreadedRenderer.sRendererEnabled || forceHwAccelerated) { if (mAttachInfo.mThreadedRenderer != null) { mAttachInfo.mThreadedRenderer.destroy(); } @@ -1808,7 +1810,7 @@ public final class ViewRootImpl implements ViewParent, mBoundsLayer = new SurfaceControl.Builder(mSurfaceSession) .setContainerLayer() .setName("Bounds for - " + getTitle().toString()) - .setParent(getRenderSurfaceControl()) + .setParent(getSurfaceControl()) .setCallsite("ViewRootImpl.getBoundsLayer") .build(); setBoundsLayerCrop(mTransaction); @@ -1818,22 +1820,19 @@ public final class ViewRootImpl implements ViewParent, } Surface getOrCreateBLASTSurface(int width, int height) { - if (mSurfaceControl == null - || !mSurfaceControl.isValid() - || mBlastSurfaceControl == null - || !mBlastSurfaceControl.isValid()) { + if (!mSurfaceControl.isValid()) { return null; } Surface ret = null; if (mBlastBufferQueue == null) { mBlastBufferQueue = new BLASTBufferQueue(mTag, - mBlastSurfaceControl, width, height, mEnableTripleBuffering); + mSurfaceControl, width, height, mEnableTripleBuffering); // We only return the Surface the first time, as otherwise // it hasn't changed and there is no need to update. ret = mBlastBufferQueue.createSurface(); } else { - mBlastBufferQueue.update(mBlastSurfaceControl, width, height); + mBlastBufferQueue.update(mSurfaceControl, width, height); } return ret; @@ -1855,7 +1854,7 @@ public final class ViewRootImpl implements ViewParent, private boolean updateBoundsLayer(SurfaceControl.Transaction t) { if (mBoundsLayer != null) { setBoundsLayerCrop(t); - t.deferTransactionUntil(mBoundsLayer, getRenderSurfaceControl(), + t.deferTransactionUntil(mBoundsLayer, getSurfaceControl(), mSurface.getNextFrameNumber()); return true; } @@ -1864,7 +1863,7 @@ public final class ViewRootImpl implements ViewParent, private void prepareSurfaces(boolean sizeChanged) { final SurfaceControl.Transaction t = mTransaction; - final SurfaceControl sc = getRenderSurfaceControl(); + final SurfaceControl sc = getSurfaceControl(); if (!sc.isValid()) return; boolean applyTransaction = updateBoundsLayer(t); @@ -1885,7 +1884,6 @@ public final class ViewRootImpl implements ViewParent, mSurface.release(); mSurfaceControl.release(); - mBlastSurfaceControl.release(); // We should probably add an explicit dispose. mBlastBufferQueue = null; } @@ -2613,7 +2611,7 @@ public final class ViewRootImpl implements ViewParent, boolean surfaceSizeChanged = false; boolean surfaceCreated = false; boolean surfaceDestroyed = false; - /* True if surface generation id changes. */ + // True if surface generation id changes or relayout result is RELAYOUT_RES_SURFACE_CHANGED. boolean surfaceReplaced = false; final boolean windowAttributesChanged = mWindowAttributesChanged; @@ -2708,6 +2706,7 @@ public final class ViewRootImpl implements ViewParent, updateColorModeIfNeeded(lp.getColorMode()); surfaceCreated = !hadSurface && mSurface.isValid(); surfaceDestroyed = hadSurface && !mSurface.isValid(); + // When using Blast, the surface generation id may not change when there's a new // SurfaceControl. In that case, we also check relayout flag // RELAYOUT_RES_SURFACE_CHANGED since it should indicate that WMS created a new @@ -2716,6 +2715,9 @@ public final class ViewRootImpl implements ViewParent, || (relayoutResult & RELAYOUT_RES_SURFACE_CHANGED) == RELAYOUT_RES_SURFACE_CHANGED) && mSurface.isValid(); + if (surfaceReplaced) { + mSurfaceSequenceId++; + } if (cutoutChanged) { mAttachInfo.mDisplayCutout.set(mPendingDisplayCutout); @@ -3815,6 +3817,89 @@ public final class ViewRootImpl implements ViewParent, } } + /** + * The callback will run on the render thread. + */ + private HardwareRenderer.FrameCompleteCallback createFrameCompleteCallback(Handler handler, + boolean reportNextDraw, ArrayList<Runnable> commitCallbacks) { + return frameNr -> { + // Use a new transaction here since mRtBLASTSyncTransaction can only be accessed by + // the render thread and mSurfaceChangedTransaction can only be accessed by the UI + // thread. The temporary transaction is used so mRtBLASTSyncTransaction can be merged + // with mSurfaceChangedTransaction without synchronization issues. + final Transaction t = new Transaction(); + finishBLASTSyncOnRT(!mSendNextFrameToWm, t); + handler.postAtFrontOfQueue(() -> { + mSurfaceChangedTransaction.merge(t); + if (reportNextDraw) { + // TODO: Use the frame number + pendingDrawFinished(); + } + if (commitCallbacks != null) { + for (int i = 0; i < commitCallbacks.size(); i++) { + commitCallbacks.get(i).run(); + } + } + }); + }; + } + + private boolean addFrameCompleteCallbackIfNeeded() { + if (mAttachInfo.mThreadedRenderer == null || !mAttachInfo.mThreadedRenderer.isEnabled()) { + return false; + } + + ArrayList<Runnable> commitCallbacks = mAttachInfo.mTreeObserver + .captureFrameCommitCallbacks(); + final boolean needFrameCompleteCallback = + mNextDrawUseBLASTSyncTransaction || mReportNextDraw + || (commitCallbacks != null && commitCallbacks.size() > 0); + if (needFrameCompleteCallback) { + mAttachInfo.mThreadedRenderer.setFrameCompleteCallback( + createFrameCompleteCallback(mAttachInfo.mHandler, mReportNextDraw, + commitCallbacks)); + return true; + } + return false; + } + + /** + * The callback will run on a worker thread pool from the render thread. + */ + private HardwareRenderer.FrameDrawingCallback createFrameDrawingCallback() { + return frame -> { + mRtNextFrameReportedConsumeWithBlast = true; + if (mBlastBufferQueue != null) { + // We don't need to synchronize mRtBLASTSyncTransaction here since it's not + // being modified and only sent to BlastBufferQueue. + mBlastBufferQueue.setNextTransaction(mRtBLASTSyncTransaction); + } + }; + } + + private void addFrameCallbackIfNeeded() { + // Frame callbacks will always occur after submitting draw requests and before + // the draw actually occurs. This will ensure that we set the next transaction + // for the frame that's about to get drawn and not on a previous frame that. + // + // This is thread safe since mRtNextFrameReportConsumeWithBlast will only be + // modified in onFrameDraw and then again in onFrameComplete. This is to ensure the + // next frame completed should be reported with the blast sync transaction. + if (mNextDrawUseBLASTSyncTransaction) { + registerRtFrameCallback(createFrameDrawingCallback()); + mNextDrawUseBLASTSyncTransaction = false; + } else if (mReportNextDraw) { + registerRtFrameCallback(frame -> { + if (mBlastBufferQueue != null) { + // If we need to report next draw, wait for adapter to flush its shadow queue + // by processing previously queued buffers so that we can submit the + // transaction a timely manner. + mBlastBufferQueue.flushShadowQueue(); + } + }); + } + } + private void performDraw() { if (mAttachInfo.mDisplayState == Display.STATE_OFF && !mReportNextDraw) { return; @@ -3828,58 +3913,14 @@ public final class ViewRootImpl implements ViewParent, mIsDrawing = true; Trace.traceBegin(Trace.TRACE_TAG_VIEW, "draw"); - boolean usingAsyncReport = false; - boolean reportNextDraw = mReportNextDraw; // Capture the original value - if (mAttachInfo.mThreadedRenderer != null && mAttachInfo.mThreadedRenderer.isEnabled()) { - ArrayList<Runnable> commitCallbacks = mAttachInfo.mTreeObserver - .captureFrameCommitCallbacks(); - final boolean needFrameCompleteCallback = mNextDrawUseBLASTSyncTransaction || - (commitCallbacks != null && commitCallbacks.size() > 0) || - mReportNextDraw; - usingAsyncReport = mReportNextDraw; - if (needFrameCompleteCallback) { - final Handler handler = mAttachInfo.mHandler; - mAttachInfo.mThreadedRenderer.setFrameCompleteCallback((long frameNr) -> { - finishBLASTSync(!mSendNextFrameToWm); - handler.postAtFrontOfQueue(() -> { - if (reportNextDraw) { - // TODO: Use the frame number - pendingDrawFinished(); - } - if (commitCallbacks != null) { - for (int i = 0; i < commitCallbacks.size(); i++) { - commitCallbacks.get(i).run(); - } - } - }); - }); - } - } + boolean usingAsyncReport = addFrameCompleteCallbackIfNeeded(); + addFrameCallbackIfNeeded(); try { - if (mNextDrawUseBLASTSyncTransaction) { - // Frame callbacks will always occur after submitting draw requests and before - // the draw actually occurs. This will ensure that we set the next transaction - // for the frame that's about to get drawn and not on a previous frame that. - // - // This is thread safe since mRtNextFrameReportConsumeWithBlast will only be - // modified in onFrameDraw and then again in onFrameComplete. This is to ensure the - // next frame completed should be reported with the blast sync transaction. - registerRtFrameCallback(frame -> { - mRtNextFrameReportedConsumeWithBlast = true; - if (mBlastBufferQueue != null) { - // We don't need to synchronize mRtBLASTSyncTransaction here since it's not - // being modified and only sent to BlastBufferQueue. - mBlastBufferQueue.setNextTransaction(mRtBLASTSyncTransaction); - } - }); - mNextDrawUseBLASTSyncTransaction = false; - } boolean canUseAsync = draw(fullRedrawNeeded); if (usingAsyncReport && !canUseAsync) { mAttachInfo.mThreadedRenderer.setFrameCompleteCallback(null); usingAsyncReport = false; - finishBLASTSync(true /* apply */); } } finally { mIsDrawing = false; @@ -5151,7 +5192,7 @@ public final class ViewRootImpl implements ViewParent, updateLocationInParentDisplay(msg.arg1, msg.arg2); } break; case MSG_REQUEST_SCROLL_CAPTURE: - handleScrollCaptureRequest((IScrollCaptureController) msg.obj); + handleScrollCaptureRequest((IScrollCaptureCallbacks) msg.obj); break; } } @@ -7447,7 +7488,7 @@ public final class ViewRootImpl implements ViewParent, (int) (mView.getMeasuredHeight() * appScale + 0.5f), viewVisibility, insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0, frameNumber, mTmpFrames, mPendingMergedConfiguration, mSurfaceControl, mTempInsets, - mTempControls, mSurfaceSize, mBlastSurfaceControl); + mTempControls, mSurfaceSize); mPendingDisplayCutout.set(mTmpFrames.displayCutout); mPendingBackDropFrame.set(mTmpFrames.backdropFrame); if (mSurfaceControl.isValid()) { @@ -8938,10 +8979,10 @@ public final class ViewRootImpl implements ViewParent, /** * Dispatches a scroll capture request to the view hierarchy on the ui thread. * - * @param controller the controller to receive replies + * @param callbacks for replies */ - public void dispatchScrollCaptureRequest(@NonNull IScrollCaptureController controller) { - mHandler.obtainMessage(MSG_REQUEST_SCROLL_CAPTURE, controller).sendToTarget(); + public void dispatchScrollCaptureRequest(@NonNull IScrollCaptureCallbacks callbacks) { + mHandler.obtainMessage(MSG_REQUEST_SCROLL_CAPTURE, callbacks).sendToTarget(); } /** @@ -8966,14 +9007,14 @@ public final class ViewRootImpl implements ViewParent, * Handles an inbound request for scroll capture from the system. If a client is not already * active, a search will be dispatched through the view tree to locate scrolling content. * <p> - * Either {@link IScrollCaptureController#onClientConnected(IScrollCaptureClient, Rect, - * Point)} or {@link IScrollCaptureController#onClientUnavailable()} will be returned + * Either {@link IScrollCaptureCallbacks#onClientConnected(IScrollCaptureConnection, Rect, + * Point)} or {@link IScrollCaptureCallbacks#onUnavailable()} will be returned * depending on the results of the search. * - * @param controller the interface to the system controller + * @param callbacks to receive responses * @see ScrollCaptureTargetResolver */ - private void handleScrollCaptureRequest(@NonNull IScrollCaptureController controller) { + private void handleScrollCaptureRequest(@NonNull IScrollCaptureCallbacks callbacks) { LinkedList<ScrollCaptureTarget> targetList = new LinkedList<>(); // Window (root) level callbacks @@ -8988,7 +9029,7 @@ public final class ViewRootImpl implements ViewParent, // No-op path. Scroll capture not offered for this window. if (targetList.isEmpty()) { - dispatchScrollCaptureSearchResult(controller, null); + dispatchScrollCaptureSearchResult(callbacks, null); return; } @@ -8996,12 +9037,12 @@ public final class ViewRootImpl implements ViewParent, // Continues with the consumer once all responses are consumed, or the timeout expires. ScrollCaptureTargetResolver resolver = new ScrollCaptureTargetResolver(targetList); resolver.start(mHandler, 1000, - (selected) -> dispatchScrollCaptureSearchResult(controller, selected)); + (selected) -> dispatchScrollCaptureSearchResult(callbacks, selected)); } /** Called by {@link #handleScrollCaptureRequest} when a result is returned */ private void dispatchScrollCaptureSearchResult( - @NonNull IScrollCaptureController controller, + @NonNull IScrollCaptureCallbacks callbacks, @Nullable ScrollCaptureTarget selectedTarget) { // If timeout or no eligible targets found. @@ -9010,31 +9051,31 @@ public final class ViewRootImpl implements ViewParent, if (DEBUG_SCROLL_CAPTURE) { Log.d(TAG, "scrollCaptureSearch returned no targets available."); } - controller.onClientUnavailable(); + callbacks.onUnavailable(); } catch (RemoteException e) { if (DEBUG_SCROLL_CAPTURE) { - Log.w(TAG, "Failed to notify controller of scroll capture search result.", e); + Log.w(TAG, "Failed to send scroll capture search result.", e); } } return; } // Create a client instance and return it to the caller - mScrollCaptureClient = new ScrollCaptureClient(selectedTarget, controller); + mScrollCaptureConnection = new ScrollCaptureConnection(selectedTarget, callbacks); try { if (DEBUG_SCROLL_CAPTURE) { - Log.d(TAG, "scrollCaptureSearch returning client: " + getScrollCaptureClient()); + Log.d(TAG, "scrollCaptureSearch returning client: " + getScrollCaptureConnection()); } - controller.onClientConnected( - mScrollCaptureClient, + callbacks.onConnected( + mScrollCaptureConnection, selectedTarget.getScrollBounds(), selectedTarget.getPositionInWindow()); } catch (RemoteException e) { if (DEBUG_SCROLL_CAPTURE) { - Log.w(TAG, "Failed to notify controller of scroll capture search result.", e); + Log.w(TAG, "Failed to send scroll capture search result.", e); } - mScrollCaptureClient.disconnect(); - mScrollCaptureClient = null; + mScrollCaptureConnection.disconnect(); + mScrollCaptureConnection = null; } } @@ -9332,10 +9373,10 @@ public final class ViewRootImpl implements ViewParent, } @Override - public void requestScrollCapture(IScrollCaptureController controller) { + public void requestScrollCapture(IScrollCaptureCallbacks callbacks) { final ViewRootImpl viewAncestor = mViewAncestor.get(); if (viewAncestor != null) { - viewAncestor.dispatchScrollCaptureRequest(controller); + viewAncestor.dispatchScrollCaptureRequest(callbacks); } } } @@ -9844,7 +9885,12 @@ public final class ViewRootImpl implements ViewParent, mNextDrawUseBLASTSyncTransaction = true; } - private void finishBLASTSync(boolean apply) { + /** + * This should only be called from the render thread. + */ + private void finishBLASTSyncOnRT(boolean apply, Transaction t) { + // This is safe to modify on the render thread since the only other place it's modified + // is on the UI thread when the render thread is paused. mSendNextFrameToWm = false; if (mRtNextFrameReportedConsumeWithBlast) { mRtNextFrameReportedConsumeWithBlast = false; @@ -9855,7 +9901,7 @@ public final class ViewRootImpl implements ViewParent, if (apply) { mRtBLASTSyncTransaction.apply(); } else { - mSurfaceChangedTransaction.merge(mRtBLASTSyncTransaction); + t.merge(mRtBLASTSyncTransaction); } } } @@ -9868,17 +9914,6 @@ public final class ViewRootImpl implements ViewParent, return mRtBLASTSyncTransaction; } - /** - * @hide - */ - public SurfaceControl getRenderSurfaceControl() { - if (useBLAST()) { - return mBlastSurfaceControl; - } else { - return mSurfaceControl; - } - } - @Override public void onDescendantUnbufferedRequested() { mUnbufferedInputSource = mView.mUnbufferedInputSource; @@ -9895,4 +9930,8 @@ public final class ViewRootImpl implements ViewParent, boolean useBLAST() { return mUseBLASTAdapter && !mForceDisableBLAST; } + + int getSurfaceSequenceId() { + return mSurfaceSequenceId; + } } diff --git a/core/java/android/view/ViewRootInsetsControllerHost.java b/core/java/android/view/ViewRootInsetsControllerHost.java index 90a80cefc54d..8f58df466ee3 100644 --- a/core/java/android/view/ViewRootInsetsControllerHost.java +++ b/core/java/android/view/ViewRootInsetsControllerHost.java @@ -144,7 +144,9 @@ public class ViewRootInsetsControllerHost implements InsetsController.Host { @Override public void onInsetsModified(InsetsState insetsState) { try { - mViewRoot.mWindowSession.insetsModified(mViewRoot.mWindow, insetsState); + if (mViewRoot.mAdded) { + mViewRoot.mWindowSession.insetsModified(mViewRoot.mWindow, insetsState); + } } catch (RemoteException e) { Log.e(TAG, "Failed to call insetsModified", e); } diff --git a/core/java/android/view/ViewTreeObserver.java b/core/java/android/view/ViewTreeObserver.java index d7b0afc89eaa..d9b55e4e729d 100644 --- a/core/java/android/view/ViewTreeObserver.java +++ b/core/java/android/view/ViewTreeObserver.java @@ -307,7 +307,7 @@ public final class ViewTreeObserver { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java index 1dbf37aca689..5331a1b8d538 100644 --- a/core/java/android/view/Window.java +++ b/core/java/android/view/Window.java @@ -2560,19 +2560,20 @@ public abstract class Window { /** * System request to begin scroll capture. * - * @param controller the controller to receive responses + * @param callbacks to receive responses * @hide */ - public void requestScrollCapture(IScrollCaptureController controller) { + public void requestScrollCapture(IScrollCaptureCallbacks callbacks) { } /** - * Registers a {@link ScrollCaptureCallback} with the root of this window. + * Used to provide scroll capture support for an arbitrary window. This registeres the given + * callback with the root view of the window. * * @param callback the callback to add * @hide */ - public void addScrollCaptureCallback(@NonNull ScrollCaptureCallback callback) { + public void registerScrollCaptureCallback(@NonNull ScrollCaptureCallback callback) { } /** @@ -2581,7 +2582,7 @@ public abstract class Window { * @param callback the callback to remove * @hide */ - public void removeScrollCaptureCallback(@NonNull ScrollCaptureCallback callback) { + public void unregisterScrollCaptureCallback(@NonNull ScrollCaptureCallback callback) { } /** @hide */ diff --git a/core/java/android/view/WindowInsets.java b/core/java/android/view/WindowInsets.java index 5e94758b003c..94c518483429 100644 --- a/core/java/android/view/WindowInsets.java +++ b/core/java/android/view/WindowInsets.java @@ -942,7 +942,7 @@ public final class WindowInsets { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (this == o) return true; if (o == null || !(o instanceof WindowInsets)) return false; WindowInsets that = (WindowInsets) o; diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index e96e98b437a1..ba3dee42c0b2 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -224,12 +224,6 @@ public interface WindowManager extends ViewManager { 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 */ @@ -302,7 +296,6 @@ public interface WindowManager extends ViewManager { TRANSIT_WALLPAPER_INTRA_CLOSE, TRANSIT_TASK_OPEN_BEHIND, TRANSIT_ACTIVITY_RELAUNCH, - TRANSIT_DOCK_TASK_FROM_RECENTS, TRANSIT_KEYGUARD_GOING_AWAY, TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER, TRANSIT_KEYGUARD_OCCLUDE, @@ -2939,6 +2932,13 @@ public interface WindowManager extends ViewManager { public boolean preferMinimalPostProcessing = false; /** + * Indicates that this window wants to have blurred content behind it. + * + * @hide + */ + public int backgroundBlurRadius = 0; + + /** * The color mode requested by this window. The target display may * not be able to honor the request. When the color mode is not set * to {@link ActivityInfo#COLOR_MODE_DEFAULT}, it might override the @@ -3262,6 +3262,7 @@ public interface WindowManager extends ViewManager { out.writeInt(mFitInsetsSides); out.writeBoolean(mFitInsetsIgnoringVisibility); out.writeBoolean(preferMinimalPostProcessing); + out.writeInt(backgroundBlurRadius); if (providesInsetsTypes != null) { out.writeInt(providesInsetsTypes.length); out.writeIntArray(providesInsetsTypes); @@ -3329,6 +3330,7 @@ public interface WindowManager extends ViewManager { mFitInsetsSides = in.readInt(); mFitInsetsIgnoringVisibility = in.readBoolean(); preferMinimalPostProcessing = in.readBoolean(); + backgroundBlurRadius = in.readInt(); int insetsTypesLength = in.readInt(); if (insetsTypesLength > 0) { providesInsetsTypes = new int[insetsTypesLength]; @@ -3381,6 +3383,8 @@ public interface WindowManager extends ViewManager { public static final int INSET_FLAGS_CHANGED = 1 << 27; /** {@hide} */ public static final int MINIMAL_POST_PROCESSING_PREFERENCE_CHANGED = 1 << 28; + /** {@hide} */ + public static final int BACKGROUND_BLUR_RADIUS_CHANGED = 1 << 29; // internal buffer to backup/restore parameters under compatibility mode. private int[] mCompatibilityParamsBackup = null; @@ -3566,6 +3570,11 @@ public interface WindowManager extends ViewManager { changes |= MINIMAL_POST_PROCESSING_PREFERENCE_CHANGED; } + if (backgroundBlurRadius != o.backgroundBlurRadius) { + backgroundBlurRadius = o.backgroundBlurRadius; + changes |= BACKGROUND_BLUR_RADIUS_CHANGED; + } + // This can't change, it's only set at window creation time. hideTimeoutMilliseconds = o.hideTimeoutMilliseconds; @@ -3729,6 +3738,10 @@ public interface WindowManager extends ViewManager { sb.append(" preferMinimalPostProcessing="); sb.append(preferMinimalPostProcessing); } + if (backgroundBlurRadius != 0) { + sb.append(" backgroundBlurRadius="); + sb.append(backgroundBlurRadius); + } sb.append(System.lineSeparator()); sb.append(prefix).append(" fl=").append( ViewDebug.flagsToString(LayoutParams.class, "flags", flags)); diff --git a/core/java/android/view/WindowManagerGlobal.java b/core/java/android/view/WindowManagerGlobal.java index 8490f2abeffa..f01cbcc1430e 100644 --- a/core/java/android/view/WindowManagerGlobal.java +++ b/core/java/android/view/WindowManagerGlobal.java @@ -527,7 +527,7 @@ public final class WindowManagerGlobal { } allViewsRemoved = mRoots.isEmpty(); } - if (ThreadedRenderer.sTrimForeground && ThreadedRenderer.isAvailable()) { + if (ThreadedRenderer.sTrimForeground) { doTrimForeground(); } @@ -561,29 +561,28 @@ public final class WindowManagerGlobal { @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) public void trimMemory(int level) { - if (ThreadedRenderer.isAvailable()) { - if (shouldDestroyEglContext(level)) { - // Destroy all hardware surfaces and resources associated to - // known windows - synchronized (mLock) { - for (int i = mRoots.size() - 1; i >= 0; --i) { - mRoots.get(i).destroyHardwareResources(); - } + + if (shouldDestroyEglContext(level)) { + // Destroy all hardware surfaces and resources associated to + // known windows + synchronized (mLock) { + for (int i = mRoots.size() - 1; i >= 0; --i) { + mRoots.get(i).destroyHardwareResources(); } - // Force a full memory flush - level = ComponentCallbacks2.TRIM_MEMORY_COMPLETE; } + // Force a full memory flush + level = ComponentCallbacks2.TRIM_MEMORY_COMPLETE; + } - ThreadedRenderer.trimMemory(level); + ThreadedRenderer.trimMemory(level); - if (ThreadedRenderer.sTrimForeground) { - doTrimForeground(); - } + if (ThreadedRenderer.sTrimForeground) { + doTrimForeground(); } } public static void trimForeground() { - if (ThreadedRenderer.sTrimForeground && ThreadedRenderer.isAvailable()) { + if (ThreadedRenderer.sTrimForeground) { WindowManagerGlobal wm = WindowManagerGlobal.getInstance(); wm.doTrimForeground(); } diff --git a/core/java/android/view/WindowlessWindowManager.java b/core/java/android/view/WindowlessWindowManager.java index dbd8184e57bb..814787347b75 100644 --- a/core/java/android/view/WindowlessWindowManager.java +++ b/core/java/android/view/WindowlessWindowManager.java @@ -130,8 +130,8 @@ public class WindowlessWindowManager implements IWindowSession { */ @Override public int addToDisplay(IWindow window, WindowManager.LayoutParams attrs, - int viewVisibility, int displayId, Rect outFrame, Rect outContentInsets, - Rect outStableInsets, + int viewVisibility, int displayId, InsetsState requestedVisibility, Rect outFrame, + Rect outContentInsets, Rect outStableInsets, DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel, InsetsState outInsetsState, InsetsSourceControl[] outActiveControls) { final SurfaceControl.Builder b = new SurfaceControl.Builder(mSurfaceSession) @@ -166,11 +166,11 @@ public class WindowlessWindowManager implements IWindowSession { */ @Override public int addToDisplayAsUser(IWindow window, WindowManager.LayoutParams attrs, - int viewVisibility, int displayId, int userId, Rect outFrame, - Rect outContentInsets, Rect outStableInsets, + int viewVisibility, int displayId, int userId, InsetsState requestedVisibility, + Rect outFrame, Rect outContentInsets, Rect outStableInsets, DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel, InsetsState outInsetsState, InsetsSourceControl[] outActiveControls) { - return addToDisplay(window, attrs, viewVisibility, displayId, + return addToDisplay(window, attrs, viewVisibility, displayId, requestedVisibility, outFrame, outContentInsets, outStableInsets, outDisplayCutout, outInputChannel, outInsetsState, outActiveControls); } @@ -227,8 +227,7 @@ public class WindowlessWindowManager implements IWindowSession { int requestedWidth, int requestedHeight, int viewFlags, int flags, long frameNumber, ClientWindowFrames outFrames, MergedConfiguration mergedConfiguration, SurfaceControl outSurfaceControl, InsetsState outInsetsState, - InsetsSourceControl[] outActiveControls, Point outSurfaceSize, - SurfaceControl outBLASTSurfaceControl) { + InsetsSourceControl[] outActiveControls, Point outSurfaceSize) { final State state; synchronized (this) { state = mStateForWindow.get(window.asBinder()); diff --git a/core/java/android/view/accessibility/AccessibilityEvent.java b/core/java/android/view/accessibility/AccessibilityEvent.java index d80d2300dbce..f6d6fde6435f 100644 --- a/core/java/android/view/accessibility/AccessibilityEvent.java +++ b/core/java/android/view/accessibility/AccessibilityEvent.java @@ -199,7 +199,7 @@ import java.util.List; * <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 + * {@link android.view.View#setAccessibilityPaneTitle(CharSequence)}. Note 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> diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java index aed9b89747d0..a9e8d5498a57 100644 --- a/core/java/android/view/accessibility/AccessibilityManager.java +++ b/core/java/android/view/accessibility/AccessibilityManager.java @@ -1249,7 +1249,6 @@ public final class AccessibilityManager { * @hide */ @SystemApi - @TestApi @RequiresPermission(Manifest.permission.MANAGE_ACCESSIBILITY) public void performAccessibilityShortcut() { performAccessibilityShortcut(null); @@ -1294,7 +1293,6 @@ public final class AccessibilityManager { * @hide */ @SystemApi - @TestApi @RequiresPermission(Manifest.permission.MANAGE_ACCESSIBILITY) public void registerSystemAction(@NonNull RemoteAction action, int actionId) { final IAccessibilityManager service; @@ -1322,7 +1320,6 @@ public final class AccessibilityManager { * @hide */ @SystemApi - @TestApi @RequiresPermission(Manifest.permission.MANAGE_ACCESSIBILITY) public void unregisterSystemAction(int actionId) { final IAccessibilityManager service; diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java index 2d0f05e3dc02..303ba9e2a7d8 100644 --- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java +++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java @@ -4369,7 +4369,7 @@ public class AccessibilityNodeInfo implements Parcelable { } @Override - public boolean equals(Object object) { + public boolean equals(@Nullable Object object) { if (this == object) { return true; } @@ -5039,7 +5039,7 @@ public class AccessibilityNodeInfo implements Parcelable { } @Override - public boolean equals(Object other) { + public boolean equals(@Nullable Object other) { if (other == null) { return false; } diff --git a/core/java/android/view/accessibility/AccessibilityWindowInfo.java b/core/java/android/view/accessibility/AccessibilityWindowInfo.java index 813234f1f49c..ccc7a36845c0 100644 --- a/core/java/android/view/accessibility/AccessibilityWindowInfo.java +++ b/core/java/android/view/accessibility/AccessibilityWindowInfo.java @@ -655,7 +655,7 @@ public final class AccessibilityWindowInfo implements Parcelable { } @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { if (this == obj) { return true; } diff --git a/core/java/android/view/autofill/AutofillId.java b/core/java/android/view/autofill/AutofillId.java index 32b9cf7cdbb0..82d52b67c06a 100644 --- a/core/java/android/view/autofill/AutofillId.java +++ b/core/java/android/view/autofill/AutofillId.java @@ -185,7 +185,7 @@ public final class AutofillId implements Parcelable { } @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java index 9ba886aba81a..fb66b5298839 100644 --- a/core/java/android/view/autofill/AutofillManager.java +++ b/core/java/android/view/autofill/AutofillManager.java @@ -2076,7 +2076,6 @@ public final class AutofillManager { * @hide */ @SystemApi - @TestApi public void setAugmentedAutofillWhitelist(@Nullable Set<String> packages, @Nullable Set<ComponentName> activities) { if (!hasAutofillFeature()) { diff --git a/core/java/android/view/autofill/AutofillValue.java b/core/java/android/view/autofill/AutofillValue.java index 296704119e7c..be1a2f221587 100644 --- a/core/java/android/view/autofill/AutofillValue.java +++ b/core/java/android/view/autofill/AutofillValue.java @@ -184,7 +184,7 @@ public final class AutofillValue implements Parcelable { } @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; diff --git a/core/java/android/view/contentcapture/ContentCaptureCondition.java b/core/java/android/view/contentcapture/ContentCaptureCondition.java index 54ebf55a0063..1adef94255e0 100644 --- a/core/java/android/view/contentcapture/ContentCaptureCondition.java +++ b/core/java/android/view/contentcapture/ContentCaptureCondition.java @@ -17,6 +17,7 @@ package android.view.contentcapture; import android.annotation.IntDef; import android.annotation.NonNull; +import android.annotation.Nullable; import android.content.LocusId; import android.os.Parcel; import android.os.Parcelable; @@ -91,7 +92,7 @@ public final class ContentCaptureCondition implements Parcelable { } @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; diff --git a/core/java/android/view/contentcapture/ContentCaptureContext.java b/core/java/android/view/contentcapture/ContentCaptureContext.java index b84cb88ccd84..9bf3626fe868 100644 --- a/core/java/android/view/contentcapture/ContentCaptureContext.java +++ b/core/java/android/view/contentcapture/ContentCaptureContext.java @@ -21,7 +21,6 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.app.TaskInfo; import android.content.ComponentName; import android.content.Context; @@ -58,7 +57,6 @@ public final class ContentCaptureContext implements Parcelable { * @hide */ @SystemApi - @TestApi public static final int FLAG_DISABLED_BY_APP = 0x1; /** @@ -69,7 +67,6 @@ public final class ContentCaptureContext implements Parcelable { * @hide */ @SystemApi - @TestApi public static final int FLAG_DISABLED_BY_FLAG_SECURE = 0x2; /** @@ -79,7 +76,6 @@ public final class ContentCaptureContext implements Parcelable { * @hide */ @SystemApi - @TestApi public static final int FLAG_RECONNECTED = 0x4; /** @hide */ @@ -173,7 +169,6 @@ public final class ContentCaptureContext implements Parcelable { * @hide */ @SystemApi - @TestApi public int getTaskId() { return mTaskId; } @@ -184,7 +179,6 @@ public final class ContentCaptureContext implements Parcelable { * @hide */ @SystemApi - @TestApi public @Nullable ComponentName getActivityComponent() { return mComponentName; } @@ -197,7 +191,6 @@ public final class ContentCaptureContext implements Parcelable { * @hide */ @SystemApi - @TestApi public @Nullable ContentCaptureSessionId getParentSessionId() { return mParentSessionId == NO_SESSION_ID ? null : new ContentCaptureSessionId(mParentSessionId); @@ -215,7 +208,6 @@ public final class ContentCaptureContext implements Parcelable { * @hide */ @SystemApi - @TestApi public int getDisplayId() { return mDisplayId; } @@ -229,7 +221,6 @@ public final class ContentCaptureContext implements Parcelable { * @hide */ @SystemApi - @TestApi public @ContextCreationFlags int getFlags() { return mFlags; } diff --git a/core/java/android/view/contentcapture/ContentCaptureEvent.java b/core/java/android/view/contentcapture/ContentCaptureEvent.java index f49b1beee8ad..2b12230510bf 100644 --- a/core/java/android/view/contentcapture/ContentCaptureEvent.java +++ b/core/java/android/view/contentcapture/ContentCaptureEvent.java @@ -22,7 +22,6 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.graphics.Insets; import android.os.Parcel; import android.os.Parcelable; @@ -39,7 +38,6 @@ import java.util.List; /** @hide */ @SystemApi -@TestApi public final class ContentCaptureEvent implements Parcelable { private static final String TAG = ContentCaptureEvent.class.getSimpleName(); diff --git a/core/java/android/view/contentcapture/ContentCaptureManager.java b/core/java/android/view/contentcapture/ContentCaptureManager.java index 029552d4902e..10f6c610d5d3 100644 --- a/core/java/android/view/contentcapture/ContentCaptureManager.java +++ b/core/java/android/view/contentcapture/ContentCaptureManager.java @@ -644,7 +644,6 @@ public final class ContentCaptureManager { * @hide */ @SystemApi - @TestApi public boolean isContentCaptureFeatureEnabled() { final SyncResultReceiver resultReceiver = syncRun( (r) -> mService.isContentCaptureFeatureEnabled(r)); diff --git a/core/java/android/view/contentcapture/ContentCaptureSessionId.java b/core/java/android/view/contentcapture/ContentCaptureSessionId.java index 2d350b27c4a3..413a2f3aa6bc 100644 --- a/core/java/android/view/contentcapture/ContentCaptureSessionId.java +++ b/core/java/android/view/contentcapture/ContentCaptureSessionId.java @@ -17,6 +17,7 @@ package android.view.contentcapture; import android.annotation.NonNull; +import android.annotation.Nullable; import android.os.Parcel; import android.os.Parcelable; @@ -56,7 +57,7 @@ public final class ContentCaptureSessionId implements Parcelable { } @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; diff --git a/core/java/android/view/contentcapture/ViewNode.java b/core/java/android/view/contentcapture/ViewNode.java index e035c620d1fd..e731d4b76fb9 100644 --- a/core/java/android/view/contentcapture/ViewNode.java +++ b/core/java/android/view/contentcapture/ViewNode.java @@ -42,7 +42,6 @@ import com.android.internal.util.Preconditions; // instead /** @hide */ @SystemApi -@TestApi public final class ViewNode extends AssistStructure.ViewNode { private static final String TAG = ViewNode.class.getSimpleName(); diff --git a/core/java/android/view/inputmethod/CursorAnchorInfo.java b/core/java/android/view/inputmethod/CursorAnchorInfo.java index 21ead4c62366..c8c1d8745273 100644 --- a/core/java/android/view/inputmethod/CursorAnchorInfo.java +++ b/core/java/android/view/inputmethod/CursorAnchorInfo.java @@ -183,7 +183,7 @@ public final class CursorAnchorInfo implements Parcelable { } @Override - public boolean equals(Object obj){ + public boolean equals(@Nullable Object obj){ if (obj == null) { return false; } diff --git a/core/java/android/view/inputmethod/InputMethodInfo.java b/core/java/android/view/inputmethod/InputMethodInfo.java index 7cc347d25458..5d876a6f62d3 100644 --- a/core/java/android/view/inputmethod/InputMethodInfo.java +++ b/core/java/android/view/inputmethod/InputMethodInfo.java @@ -17,6 +17,7 @@ package android.view.inputmethod; import android.annotation.NonNull; +import android.annotation.Nullable; import android.compat.annotation.UnsupportedAppUsage; import android.content.ComponentName; import android.content.Context; @@ -515,7 +516,7 @@ public final class InputMethodInfo implements Parcelable { * {@link InputMethodInfo} and its Id is the same to this one. */ @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (o == this) return true; if (o == null) return false; diff --git a/core/java/android/view/inputmethod/InputMethodSubtype.java b/core/java/android/view/inputmethod/InputMethodSubtype.java index 59898479c519..14abbdb6d33f 100644 --- a/core/java/android/view/inputmethod/InputMethodSubtype.java +++ b/core/java/android/view/inputmethod/InputMethodSubtype.java @@ -597,7 +597,7 @@ public final class InputMethodSubtype implements Parcelable { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (o instanceof InputMethodSubtype) { InputMethodSubtype subtype = (InputMethodSubtype) o; if (subtype.mSubtypeId != 0 || mSubtypeId != 0) { diff --git a/core/java/android/view/inputmethod/SparseRectFArray.java b/core/java/android/view/inputmethod/SparseRectFArray.java index 596ea86853e3..07d0b0695a58 100644 --- a/core/java/android/view/inputmethod/SparseRectFArray.java +++ b/core/java/android/view/inputmethod/SparseRectFArray.java @@ -16,6 +16,7 @@ package android.view.inputmethod; +import android.annotation.Nullable; import android.graphics.RectF; import android.os.Parcel; import android.os.Parcelable; @@ -92,7 +93,7 @@ public final class SparseRectFArray implements Parcelable { } @Override - public boolean equals(Object obj){ + public boolean equals(@Nullable Object obj){ if (obj == null) { return false; } diff --git a/core/java/android/view/textclassifier/SelectionEvent.java b/core/java/android/view/textclassifier/SelectionEvent.java index 6f9556b6a96e..858825b1d5ac 100644 --- a/core/java/android/view/textclassifier/SelectionEvent.java +++ b/core/java/android/view/textclassifier/SelectionEvent.java @@ -645,7 +645,7 @@ public final class SelectionEvent implements Parcelable { } @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { if (this == obj) { return true; } diff --git a/core/java/android/view/textclassifier/TextClassificationSessionId.java b/core/java/android/view/textclassifier/TextClassificationSessionId.java index aa680c9b7bc8..5fdcc3106f41 100644 --- a/core/java/android/view/textclassifier/TextClassificationSessionId.java +++ b/core/java/android/view/textclassifier/TextClassificationSessionId.java @@ -17,6 +17,7 @@ package android.view.textclassifier; import android.annotation.NonNull; +import android.annotation.Nullable; import android.os.Binder; import android.os.IBinder; import android.os.Parcel; @@ -63,7 +64,7 @@ public final class TextClassificationSessionId implements Parcelable { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; TextClassificationSessionId that = (TextClassificationSessionId) o; diff --git a/core/java/android/view/textservice/SpellCheckerSubtype.java b/core/java/android/view/textservice/SpellCheckerSubtype.java index 12fe6263643b..38a140f4eade 100644 --- a/core/java/android/view/textservice/SpellCheckerSubtype.java +++ b/core/java/android/view/textservice/SpellCheckerSubtype.java @@ -203,7 +203,7 @@ public final class SpellCheckerSubtype implements Parcelable { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (o instanceof SpellCheckerSubtype) { SpellCheckerSubtype subtype = (SpellCheckerSubtype) o; if (subtype.mSubtypeId != SUBTYPE_ID_NONE || mSubtypeId != SUBTYPE_ID_NONE) { diff --git a/core/java/android/webkit/PacProcessor.java b/core/java/android/webkit/PacProcessor.java index 28496c6f8a07..d7b4e8bba884 100644 --- a/core/java/android/webkit/PacProcessor.java +++ b/core/java/android/webkit/PacProcessor.java @@ -34,7 +34,7 @@ public interface PacProcessor { * * <p> There can only be one default {@link PacProcessor} instance. * This method will create a new instance if one did not already exist, or - * if the previous instance was released with {@link #releasePacProcessor}. + * if the previous instance was released with {@link #release}. * * @return the default PacProcessor instance. */ @@ -47,7 +47,7 @@ public interface PacProcessor { * Create a new PacProcessor instance. * * <p> The created instance needs to be released manually once it is no longer needed - * by calling {@link #releasePacProcessor} to prevent memory leaks. + * by calling {@link #release} to prevent memory leaks. * * <p> The created instance is not tied to any particular {@link Network}. * To associate {@link PacProcessor} with a {@link Network} use {@link #setNetwork} method. @@ -82,7 +82,7 @@ public interface PacProcessor { * {@link #getInstance} and {@link #getInstanceForNetwork} * for the same network will create a new instance. */ - default void releasePacProcessor() { + default void release() { throw new UnsupportedOperationException("Not implemented"); } diff --git a/core/java/android/widget/ActivityChooserModel.java b/core/java/android/widget/ActivityChooserModel.java index d87bdf482e43..9da337ad97da 100644 --- a/core/java/android/widget/ActivityChooserModel.java +++ b/core/java/android/widget/ActivityChooserModel.java @@ -16,6 +16,7 @@ package android.widget; +import android.annotation.Nullable; import android.app.ActivityManager; import android.compat.annotation.UnsupportedAppUsage; import android.content.ComponentName; @@ -839,7 +840,7 @@ public class ActivityChooserModel extends DataSetObservable { } @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { if (this == obj) { return true; } @@ -908,7 +909,7 @@ public class ActivityChooserModel extends DataSetObservable { } @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { if (this == obj) { return true; } diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java index eaa738d59577..f2955ac554fc 100644 --- a/core/java/android/widget/Editor.java +++ b/core/java/android/widget/Editor.java @@ -206,6 +206,10 @@ public class Editor { int TEXT_LINK = 2; } + // Default content insertion handler. + private final TextViewOnReceiveContentCallback mDefaultOnReceiveContentCallback = + new TextViewOnReceiveContentCallback(); + // Each Editor manages its own undo stack. private final UndoManager mUndoManager = new UndoManager(); private UndoOwner mUndoOwner = mUndoManager.getOwner(UNDO_OWNER_TAG, this); @@ -584,6 +588,11 @@ public class Editor { mUndoOwner = mUndoManager.getOwner(UNDO_OWNER_TAG, this); } + @VisibleForTesting + public @NonNull TextViewOnReceiveContentCallback getDefaultOnReceiveContentCallback() { + return mDefaultOnReceiveContentCallback; + } + /** * Forgets all undo and redo operations for this Editor. */ @@ -709,6 +718,8 @@ public class Editor { hideCursorAndSpanControllers(); stopTextActionModeWithPreservingSelection(); + + mDefaultOnReceiveContentCallback.clearInputConnectionInfo(); } private void discardTextDisplayLists() { diff --git a/core/java/android/widget/GridLayout.java b/core/java/android/widget/GridLayout.java index f1321979e2de..a6dce7f05f4a 100644 --- a/core/java/android/widget/GridLayout.java +++ b/core/java/android/widget/GridLayout.java @@ -31,6 +31,7 @@ import static java.lang.Math.max; import static java.lang.Math.min; import android.annotation.IntDef; +import android.annotation.Nullable; import android.compat.annotation.UnsupportedAppUsage; import android.content.Context; import android.content.res.TypedArray; @@ -2221,7 +2222,7 @@ public class GridLayout extends ViewGroup { } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; @@ -2496,7 +2497,7 @@ public class GridLayout extends ViewGroup { * {@code Interval}, {@code false} otherwise. */ @Override - public boolean equals(Object that) { + public boolean equals(@Nullable Object that) { if (this == that) { return true; } @@ -2609,7 +2610,7 @@ public class GridLayout extends ViewGroup { * {@code Spec}; {@code false} otherwise */ @Override - public boolean equals(Object that) { + public boolean equals(@Nullable Object that) { if (this == that) { return true; } diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java index 7016c5cf0de6..dcfb3872bda0 100644 --- a/core/java/android/widget/RemoteViews.java +++ b/core/java/android/widget/RemoteViews.java @@ -21,6 +21,7 @@ import android.annotation.DimenRes; import android.annotation.IntDef; import android.annotation.LayoutRes; import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.StyleRes; import android.app.Activity; import android.app.ActivityOptions; @@ -348,7 +349,7 @@ public class RemoteViews implements Parcelable, Filter { public String methodName; @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (!(o instanceof MethodKey)) { return false; } diff --git a/core/java/android/widget/RemoteViewsAdapter.java b/core/java/android/widget/RemoteViewsAdapter.java index b4379ec75205..b8849367b7b6 100644 --- a/core/java/android/widget/RemoteViewsAdapter.java +++ b/core/java/android/widget/RemoteViewsAdapter.java @@ -19,6 +19,7 @@ package android.widget; import static android.widget.RemoteViews.EXTRA_REMOTEADAPTER_APPWIDGET_ID; import static android.widget.RemoteViews.EXTRA_REMOTEADAPTER_ON_LIGHT_BACKGROUND; +import android.annotation.Nullable; import android.annotation.WorkerThread; import android.app.IServiceConnection; import android.appwidget.AppWidgetHostView; @@ -814,7 +815,7 @@ public class RemoteViewsAdapter extends BaseAdapter implements Handler.Callback } @Override - public boolean equals(Object o) { + public boolean equals(@Nullable Object o) { if (!(o instanceof RemoteViewsCacheKey)) { return false; } diff --git a/core/java/android/widget/SearchView.java b/core/java/android/widget/SearchView.java index 2eadb56c0f36..b8a324931f86 100755 --- a/core/java/android/widget/SearchView.java +++ b/core/java/android/widget/SearchView.java @@ -1695,7 +1695,7 @@ public class SearchView extends LinearLayout implements CollapsibleActionView { Intent queryIntent = new Intent(Intent.ACTION_SEARCH); queryIntent.setComponent(searchActivity); PendingIntent pending = PendingIntent.getActivity(getContext(), 0, queryIntent, - PendingIntent.FLAG_ONE_SHOT); + PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_MUTABLE); // Now set up the bundle that will be inserted into the pending intent // when it's time to do the search. We always build it here (even if empty) diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 52a3f4145e7e..7bb2b7e92a00 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -80,6 +80,7 @@ import android.os.AsyncTask; import android.os.Build; import android.os.Build.VERSION_CODES; import android.os.Bundle; +import android.os.Handler; import android.os.LocaleList; import android.os.Parcel; import android.os.Parcelable; @@ -890,13 +891,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener @UnsupportedAppUsage private Editor mEditor; - /** - * The default content insertion callback used by {@link TextView}. See - * {@link #setOnReceiveContentCallback} for more info. - */ - private static final TextViewOnReceiveContentCallback DEFAULT_ON_RECEIVE_CONTENT_CALLBACK = - new TextViewOnReceiveContentCallback(); - private static final int DEVICE_PROVISIONED_UNKNOWN = 0; private static final int DEVICE_PROVISIONED_NO = 1; private static final int DEVICE_PROVISIONED_YES = 2; @@ -13723,6 +13717,23 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } } + /** @hide */ + @Override + public void onInputConnectionOpenedInternal(@NonNull InputConnection ic, + @NonNull EditorInfo editorInfo, @Nullable Handler handler) { + if (mEditor != null) { + mEditor.getDefaultOnReceiveContentCallback().setInputConnectionInfo(ic, editorInfo); + } + } + + /** @hide */ + @Override + public void onInputConnectionClosedInternal() { + if (mEditor != null) { + mEditor.getDefaultOnReceiveContentCallback().clearInputConnectionInfo(); + } + } + /** * Returns the callback used for handling insertion of content into this view. See * {@link #setOnReceiveContentCallback} for more info. @@ -13773,8 +13784,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener ClipDescription description = payload.getClip().getDescription(); if (receiver != null && receiver.supports(this, description)) { receiver.onReceiveContent(this, payload); - } else { - DEFAULT_ON_RECEIVE_CONTENT_CALLBACK.onReceiveContent(this, payload); + } else if (mEditor != null) { + mEditor.getDefaultOnReceiveContentCallback().onReceiveContent(this, payload); } } diff --git a/core/java/android/widget/TextViewOnReceiveContentCallback.java b/core/java/android/widget/TextViewOnReceiveContentCallback.java index 35618cb3d2a5..d7c95b7eae86 100644 --- a/core/java/android/widget/TextViewOnReceiveContentCallback.java +++ b/core/java/android/widget/TextViewOnReceiveContentCallback.java @@ -16,24 +16,44 @@ package android.widget; +import static android.content.ContentResolver.SCHEME_CONTENT; import static android.view.OnReceiveContentCallback.Payload.FLAG_CONVERT_TO_PLAIN_TEXT; import static android.view.OnReceiveContentCallback.Payload.SOURCE_AUTOFILL; import static android.view.OnReceiveContentCallback.Payload.SOURCE_DRAG_AND_DROP; +import static java.util.Collections.singleton; + import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.SuppressLint; +import android.compat.Compatibility; +import android.compat.annotation.ChangeId; +import android.compat.annotation.EnabledAfter; import android.content.ClipData; +import android.content.ClipDescription; import android.content.Context; +import android.net.Uri; +import android.os.Build; +import android.os.Bundle; import android.text.Editable; import android.text.Selection; import android.text.SpannableStringBuilder; import android.text.Spanned; +import android.util.ArraySet; import android.util.Log; import android.view.OnReceiveContentCallback; import android.view.OnReceiveContentCallback.Payload.Flags; import android.view.OnReceiveContentCallback.Payload.Source; +import android.view.View; +import android.view.inputmethod.EditorInfo; +import android.view.inputmethod.InputConnection; +import android.view.inputmethod.InputContentInfo; + +import com.android.internal.annotations.VisibleForTesting; -import java.util.Collections; +import java.lang.ref.WeakReference; +import java.util.ArrayList; +import java.util.Arrays; import java.util.Set; /** @@ -46,19 +66,26 @@ import java.util.Set; public class TextViewOnReceiveContentCallback implements OnReceiveContentCallback<TextView> { private static final String LOG_TAG = "OnReceiveContent"; - private static final Set<String> MIME_TYPES_ALL_TEXT = Collections.singleton("text/*"); + private static final String MIME_TYPE_ALL_TEXT = "text/*"; + private static final Set<String> MIME_TYPES_ALL_TEXT = singleton(MIME_TYPE_ALL_TEXT); + + @Nullable private InputConnectionInfo mInputConnectionInfo; + @Nullable private ArraySet<String> mCachedSupportedMimeTypes; @SuppressLint("CallbackMethodName") @NonNull @Override public Set<String> getSupportedMimeTypes(@NonNull TextView view) { - return MIME_TYPES_ALL_TEXT; + if (!isUsageOfImeCommitContentEnabled(view)) { + return MIME_TYPES_ALL_TEXT; + } + return getSupportedMimeTypesAugmentedWithImeCommitContentMimeTypes(); } @Override public boolean onReceiveContent(@NonNull TextView view, @NonNull Payload payload) { if (Log.isLoggable(LOG_TAG, Log.DEBUG)) { - Log.d(LOG_TAG, "onReceive:" + payload); + Log.d(LOG_TAG, "onReceive: " + payload); } ClipData clip = payload.getClip(); @Source int source = payload.getSource(); @@ -109,13 +136,22 @@ public class TextViewOnReceiveContentCallback implements OnReceiveContentCallbac editable.replace(start, end, replacement); } - private static boolean onReceiveForAutofill(@NonNull TextView textView, @NonNull ClipData clip, + private boolean onReceiveForAutofill(@NonNull TextView view, @NonNull ClipData clip, @Flags int flags) { - final CharSequence text = coerceToText(clip, textView.getContext(), flags); + if (isUsageOfImeCommitContentEnabled(view)) { + clip = handleNonTextViaImeCommitContent(clip); + if (clip == null) { + if (Log.isLoggable(LOG_TAG, Log.VERBOSE)) { + Log.v(LOG_TAG, "onReceive: Handled via IME"); + } + return true; + } + } + final CharSequence text = coerceToText(clip, view.getContext(), flags); // First autofill it... - textView.setText(text); + view.setText(text); // ...then move cursor to the end. - final Editable editable = (Editable) textView.getText(); + final Editable editable = (Editable) view.getText(); Selection.setSelection(editable, editable.length()); return true; } @@ -146,4 +182,250 @@ public class TextViewOnReceiveContentCallback implements OnReceiveContentCallbac } return ssb; } + + /** + * On Android S and above, the platform can provide non-text suggestions (e.g. images) via the + * augmented autofill framework (see + * <a href="/guide/topics/text/autofill-services">autofill services</a>). In order for an app to + * be able to handle these suggestions, it must normally implement the + * {@link android.view.OnReceiveContentCallback} API. To make the adoption of this smoother for + * apps that have previously implemented the + * {@link android.view.inputmethod.InputConnection#commitContent(InputContentInfo, int, Bundle)} + * API, we reuse that API as a fallback if {@link android.view.OnReceiveContentCallback} is not + * yet implemented by the app. This fallback is only enabled on Android S. This change ID + * disables the fallback, such that apps targeting Android T and above must implement the + * {@link android.view.OnReceiveContentCallback} API in order to accept non-text suggestions. + */ + @ChangeId + @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.S) // Enabled on Android T and higher + private static final long AUTOFILL_NON_TEXT_REQUIRES_ON_RECEIVE_CONTENT_CALLBACK = 163400105L; + + /** + * Returns true if we can use the IME {@link InputConnection#commitContent} API in order handle + * non-text content. + */ + private static boolean isUsageOfImeCommitContentEnabled(@NonNull View view) { + if (view.getOnReceiveContentCallback() != null) { + if (Log.isLoggable(LOG_TAG, Log.VERBOSE)) { + Log.v(LOG_TAG, "Fallback to commitContent disabled (custom callback is set)"); + } + return false; + } + if (Compatibility.isChangeEnabled(AUTOFILL_NON_TEXT_REQUIRES_ON_RECEIVE_CONTENT_CALLBACK)) { + if (Log.isLoggable(LOG_TAG, Log.VERBOSE)) { + Log.v(LOG_TAG, "Fallback to commitContent disabled (target SDK is above S)"); + } + return false; + } + return true; + } + + private static final class InputConnectionInfo { + @NonNull private final WeakReference<InputConnection> mInputConnection; + @NonNull private final String[] mEditorInfoContentMimeTypes; + + private InputConnectionInfo(@NonNull InputConnection inputConnection, + @NonNull String[] editorInfoContentMimeTypes) { + mInputConnection = new WeakReference<>(inputConnection); + mEditorInfoContentMimeTypes = editorInfoContentMimeTypes; + } + + @Override + public String toString() { + return "InputConnectionInfo{" + + "mimeTypes=" + Arrays.toString(mEditorInfoContentMimeTypes) + + ", ic=" + mInputConnection + + '}'; + } + } + + /** + * Invoked by the platform when an {@link InputConnection} is successfully created for the view + * that owns this callback instance. + */ + void setInputConnectionInfo(@NonNull InputConnection ic, @NonNull EditorInfo editorInfo) { + if (Log.isLoggable(LOG_TAG, Log.VERBOSE)) { + Log.v(LOG_TAG, "setInputConnectionInfo: " + + Arrays.toString(editorInfo.contentMimeTypes)); + } + String[] contentMimeTypes = editorInfo.contentMimeTypes; + if (contentMimeTypes == null || contentMimeTypes.length == 0) { + mInputConnectionInfo = null; + } else { + mInputConnectionInfo = new InputConnectionInfo(ic, contentMimeTypes); + } + } + + /** + * Invoked by the platform when an {@link InputConnection} is closed for the view that owns this + * callback instance. + */ + void clearInputConnectionInfo() { + if (Log.isLoggable(LOG_TAG, Log.VERBOSE)) { + Log.v(LOG_TAG, "clearInputConnectionInfo: " + mInputConnectionInfo); + } + mInputConnectionInfo = null; + } + + private Set<String> getSupportedMimeTypesAugmentedWithImeCommitContentMimeTypes() { + InputConnectionInfo icInfo = mInputConnectionInfo; + if (icInfo == null) { + if (Log.isLoggable(LOG_TAG, Log.VERBOSE)) { + Log.v(LOG_TAG, "getSupportedMimeTypes: No usable EditorInfo/InputConnection"); + } + return MIME_TYPES_ALL_TEXT; + } + String[] editorInfoContentMimeTypes = icInfo.mEditorInfoContentMimeTypes; + if (Log.isLoggable(LOG_TAG, Log.VERBOSE)) { + Log.v(LOG_TAG, "getSupportedMimeTypes: Augmenting with EditorInfo.contentMimeTypes: " + + Arrays.toString(editorInfoContentMimeTypes)); + } + ArraySet<String> supportedMimeTypes = mCachedSupportedMimeTypes; + if (canReuse(supportedMimeTypes, editorInfoContentMimeTypes)) { + return supportedMimeTypes; + } + supportedMimeTypes = new ArraySet<>(editorInfoContentMimeTypes); + supportedMimeTypes.add(MIME_TYPE_ALL_TEXT); + mCachedSupportedMimeTypes = supportedMimeTypes; + return supportedMimeTypes; + } + + /** + * We want to avoid creating a new set on every invocation of {@link #getSupportedMimeTypes}. + * This method will check if the cached set of MIME types matches the data in the given array + * from {@link EditorInfo} or if a new set should be created. The custom logic is needed for + * comparing the data because the set contains the additional "text/*" MIME type. + * + * @param cachedMimeTypes Previously cached set of MIME types. + * @param newEditorInfoMimeTypes MIME types from {@link EditorInfo}. + * + * @return Returns true if the data in the given cached set matches the data in the array. + * + * @hide + */ + @VisibleForTesting + public static boolean canReuse(@Nullable ArraySet<String> cachedMimeTypes, + @NonNull String[] newEditorInfoMimeTypes) { + if (cachedMimeTypes == null) { + return false; + } + if (newEditorInfoMimeTypes.length != cachedMimeTypes.size() + && newEditorInfoMimeTypes.length != (cachedMimeTypes.size() - 1)) { + return false; + } + final boolean ignoreAllTextMimeType = + newEditorInfoMimeTypes.length == (cachedMimeTypes.size() - 1); + for (String mimeType : cachedMimeTypes) { + if (ignoreAllTextMimeType && mimeType.equals(MIME_TYPE_ALL_TEXT)) { + continue; + } + boolean present = false; + for (String editorInfoContentMimeType : newEditorInfoMimeTypes) { + if (editorInfoContentMimeType.equals(mimeType)) { + present = true; + break; + } + } + if (!present) { + return false; + } + } + return true; + } + + /** + * Tries to insert the content in the clip into the app via the image keyboard API. If all the + * items in the clip are successfully inserted, returns null. If one or more of the items in the + * clip cannot be inserted, returns a non-null clip that contains the items that were not + * inserted. + */ + @Nullable + private ClipData handleNonTextViaImeCommitContent(@NonNull ClipData clip) { + ClipDescription description = clip.getDescription(); + if (!containsUri(clip) || containsOnlyText(clip)) { + if (Log.isLoggable(LOG_TAG, Log.VERBOSE)) { + Log.v(LOG_TAG, "onReceive: Clip doesn't contain any non-text URIs: " + + description); + } + return clip; + } + + InputConnectionInfo icInfo = mInputConnectionInfo; + InputConnection inputConnection = (icInfo != null) ? icInfo.mInputConnection.get() : null; + if (inputConnection == null) { + if (Log.isLoggable(LOG_TAG, Log.DEBUG)) { + Log.d(LOG_TAG, "onReceive: No usable EditorInfo/InputConnection"); + } + return clip; + } + String[] editorInfoContentMimeTypes = icInfo.mEditorInfoContentMimeTypes; + if (!isClipMimeTypeSupported(editorInfoContentMimeTypes, clip.getDescription())) { + if (Log.isLoggable(LOG_TAG, Log.DEBUG)) { + Log.d(LOG_TAG, + "onReceive: MIME type is not supported by the app's commitContent impl"); + } + return clip; + } + + if (Log.isLoggable(LOG_TAG, Log.VERBOSE)) { + Log.v(LOG_TAG, "onReceive: Trying to insert via IME: " + description); + } + ArrayList<ClipData.Item> remainingItems = new ArrayList<>(0); + for (int i = 0; i < clip.getItemCount(); i++) { + ClipData.Item item = clip.getItemAt(i); + Uri uri = item.getUri(); + if (uri == null || !SCHEME_CONTENT.equals(uri.getScheme())) { + if (Log.isLoggable(LOG_TAG, Log.VERBOSE)) { + Log.v(LOG_TAG, "onReceive: No content URI in item: uri=" + uri); + } + remainingItems.add(item); + continue; + } + if (Log.isLoggable(LOG_TAG, Log.VERBOSE)) { + Log.v(LOG_TAG, "onReceive: Calling commitContent: uri=" + uri); + } + InputContentInfo contentInfo = new InputContentInfo(uri, description); + if (!inputConnection.commitContent(contentInfo, 0, null)) { + if (Log.isLoggable(LOG_TAG, Log.VERBOSE)) { + Log.v(LOG_TAG, "onReceive: Call to commitContent returned false: uri=" + uri); + } + remainingItems.add(item); + } + } + if (remainingItems.isEmpty()) { + return null; + } + return new ClipData(description, remainingItems); + } + + private static boolean isClipMimeTypeSupported(@NonNull String[] supportedMimeTypes, + @NonNull ClipDescription description) { + for (String imeSupportedMimeType : supportedMimeTypes) { + if (description.hasMimeType(imeSupportedMimeType)) { + return true; + } + } + return false; + } + + private static boolean containsUri(@NonNull ClipData clip) { + for (int i = 0; i < clip.getItemCount(); i++) { + ClipData.Item item = clip.getItemAt(i); + if (item.getUri() != null) { + return true; + } + } + return false; + } + + private static boolean containsOnlyText(@NonNull ClipData clip) { + ClipDescription description = clip.getDescription(); + for (int i = 0; i < description.getMimeTypeCount(); i++) { + String mimeType = description.getMimeType(i); + if (!mimeType.startsWith("text/")) { + return false; + } + } + return true; + } } diff --git a/core/java/android/window/ITaskOrganizerController.aidl b/core/java/android/window/ITaskOrganizerController.aidl index 12b16ff6645c..b5a11b1a8136 100644 --- a/core/java/android/window/ITaskOrganizerController.aidl +++ b/core/java/android/window/ITaskOrganizerController.aidl @@ -17,7 +17,9 @@ package android.window; import android.app.ActivityManager; +import android.content.pm.ParceledListSlice; import android.window.ITaskOrganizer; +import android.window.TaskAppearedInfo; import android.window.WindowContainerToken; import android.window.WindowContainerTransaction; @@ -26,16 +28,23 @@ interface ITaskOrganizerController { /** * Register a TaskOrganizer to manage all the tasks with supported windowing modes. + * + * @return a list of the tasks that should be managed by the organizer, not including tasks + * created via {@link #createRootTask}. */ - void registerTaskOrganizer(ITaskOrganizer organizer); + ParceledListSlice<TaskAppearedInfo> registerTaskOrganizer(ITaskOrganizer organizer); /** * Unregisters a previously registered task organizer. */ void unregisterTaskOrganizer(ITaskOrganizer organizer); - /** Creates a persistent root task in WM for a particular windowing-mode. */ - ActivityManager.RunningTaskInfo createRootTask(int displayId, int windowingMode); + /** + * Creates a persistent root task in WM for a particular windowing-mode. + * {@link TaskOrganizer#onTaskAppeared} won't be called since we are returning + * {@link TaskAppearedInfo} here. + */ + TaskAppearedInfo createRootTask(int displayId, int windowingMode); /** Deletes a persistent root task in WM */ boolean deleteRootTask(in WindowContainerToken task); diff --git a/core/java/android/window/TaskAppearedInfo.aidl b/core/java/android/window/TaskAppearedInfo.aidl new file mode 100644 index 000000000000..13eba25f37a3 --- /dev/null +++ b/core/java/android/window/TaskAppearedInfo.aidl @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.window; + +/** + * Data object for the task info provided when a task is presented to an organizer. + * @hide + */ +parcelable TaskAppearedInfo; + diff --git a/core/java/android/window/TaskAppearedInfo.java b/core/java/android/window/TaskAppearedInfo.java new file mode 100644 index 000000000000..2ff331eb22e5 --- /dev/null +++ b/core/java/android/window/TaskAppearedInfo.java @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.window; + +import android.annotation.NonNull; +import android.annotation.TestApi; +import android.app.ActivityManager.RunningTaskInfo; +import android.os.Parcel; +import android.os.Parcelable; +import android.view.SurfaceControl; + +/** + * Data object for the task info provided when a task is presented to an organizer. + * @hide + */ +@TestApi +public final class TaskAppearedInfo implements Parcelable { + + @NonNull + private final RunningTaskInfo mTaskInfo; + + @NonNull + private final SurfaceControl mLeash; + + @NonNull + public static final Creator<TaskAppearedInfo> CREATOR = new Creator<TaskAppearedInfo>() { + @Override + public TaskAppearedInfo createFromParcel(Parcel source) { + final RunningTaskInfo taskInfo = source.readTypedObject(RunningTaskInfo.CREATOR); + final SurfaceControl leash = source.readTypedObject(SurfaceControl.CREATOR); + return new TaskAppearedInfo(taskInfo, leash); + } + + @Override + public TaskAppearedInfo[] newArray(int size) { + return new TaskAppearedInfo[size]; + } + + }; + + public TaskAppearedInfo(@NonNull RunningTaskInfo taskInfo, @NonNull SurfaceControl leash) { + mTaskInfo = taskInfo; + mLeash = leash; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeTypedObject(mTaskInfo, flags); + dest.writeTypedObject(mLeash, flags); + } + + @Override + public int describeContents() { + return 0; + } + + /** + * @return the task info. + */ + @NonNull + public RunningTaskInfo getTaskInfo() { + return mTaskInfo; + } + + /** + * @return the leash for the task. + */ + @NonNull + public SurfaceControl getLeash() { + return mLeash; + } +} diff --git a/core/java/android/window/TaskOrganizer.java b/core/java/android/window/TaskOrganizer.java index a7cb642b83f9..5c86e1c124f7 100644 --- a/core/java/android/window/TaskOrganizer.java +++ b/core/java/android/window/TaskOrganizer.java @@ -17,6 +17,7 @@ package android.window; import android.annotation.BinderThread; +import android.annotation.CallSuper; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; @@ -51,11 +52,16 @@ public class TaskOrganizer extends WindowOrganizer { /** * Register a TaskOrganizer to manage tasks as they enter a supported windowing mode. + * + * @return a list of the tasks that should be managed by the organizer, not including tasks + * created via {@link #createRootTask}. */ @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) - public final void registerOrganizer() { + @CallSuper + @NonNull + public List<TaskAppearedInfo> registerOrganizer() { try { - mTaskOrganizerController.registerTaskOrganizer(mInterface); + return mTaskOrganizerController.registerTaskOrganizer(mInterface).getList(); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -63,7 +69,8 @@ public class TaskOrganizer extends WindowOrganizer { /** Unregisters a previously registered task organizer. */ @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) - public final void unregisterOrganizer() { + @CallSuper + public void unregisterOrganizer() { try { mTaskOrganizerController.unregisterTaskOrganizer(mInterface); } catch (RemoteException e) { @@ -92,7 +99,7 @@ public class TaskOrganizer extends WindowOrganizer { /** Creates a persistent root task in WM for a particular windowing-mode. */ @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) @Nullable - public ActivityManager.RunningTaskInfo createRootTask(int displayId, int windowingMode) { + public TaskAppearedInfo createRootTask(int displayId, int windowingMode) { try { return mTaskOrganizerController.createRootTask(displayId, windowingMode); } catch (RemoteException e) { diff --git a/core/java/android/window/WindowContainerToken.java b/core/java/android/window/WindowContainerToken.java index 96e8b44d13cc..22b90b260f05 100644 --- a/core/java/android/window/WindowContainerToken.java +++ b/core/java/android/window/WindowContainerToken.java @@ -17,6 +17,7 @@ package android.window; import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.TestApi; import android.os.IBinder; import android.os.Parcel; @@ -83,7 +84,7 @@ public final class WindowContainerToken implements Parcelable { } @Override - public boolean equals(Object obj) { + public boolean equals(@Nullable Object obj) { if (!(obj instanceof WindowContainerToken)) { return false; } diff --git a/core/java/android/window/WindowContainerTransaction.java b/core/java/android/window/WindowContainerTransaction.java index ba901549f2b5..eba4fd21166d 100644 --- a/core/java/android/window/WindowContainerTransaction.java +++ b/core/java/android/window/WindowContainerTransaction.java @@ -238,10 +238,10 @@ public final class WindowContainerTransaction implements Parcelable { } /** - * Sets whether a container should ignore the orientation request from apps below it. It - * currently only applies to {@link com.android.server.wm.TaskDisplayArea}. When {@code false}, - * it may rotate based on the orientation request; When {@code true}, it can never specify - * orientation, but shows the fixed-orientation apps in the letterbox. + * Sets whether a container should ignore the orientation request from apps and windows below + * it. It currently only applies to {@link com.android.server.wm.DisplayArea}. When + * {@code false}, it may rotate based on the orientation request; When {@code true}, it can + * never specify orientation, but shows the fixed-orientation apps below it in the letterbox. * @hide */ @NonNull diff --git a/core/java/com/android/internal/BrightnessSynchronizer.java b/core/java/com/android/internal/BrightnessSynchronizer.java index 6b8cf6361e91..c98477e8c343 100644 --- a/core/java/com/android/internal/BrightnessSynchronizer.java +++ b/core/java/com/android/internal/BrightnessSynchronizer.java @@ -246,10 +246,12 @@ public class BrightnessSynchronizer { } if (BRIGHTNESS_URI.equals(uri)) { int currentBrightness = getScreenBrightnessInt(mContext); + mHandler.removeMessages(MSG_UPDATE_FLOAT); mHandler.obtainMessage(MSG_UPDATE_FLOAT, currentBrightness, 0).sendToTarget(); } else if (BRIGHTNESS_FLOAT_URI.equals(uri)) { float currentFloat = getScreenBrightnessFloat(mContext); int toSend = Float.floatToIntBits(currentFloat); + mHandler.removeMessages(MSG_UPDATE_INT); mHandler.obtainMessage(MSG_UPDATE_INT, toSend, 0).sendToTarget(); } } diff --git a/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java index 491ddba8959d..2b4e09d66851 100644 --- a/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java +++ b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java @@ -80,6 +80,8 @@ public class AccessibilityShortcutController { "com.android.server.accessibility.MagnificationController"; public static final ComponentName MAGNIFICATION_COMPONENT_NAME = new ComponentName("com.android.server.accessibility", "Magnification"); + public static final ComponentName REDUCE_BRIGHT_COLORS_COMPONENT_NAME = + new ComponentName("com.android.server.accessibility", "ReduceBrightColors"); private static final AudioAttributes VIBRATION_ATTRIBUTES = new AudioAttributes.Builder() .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION) @@ -126,6 +128,11 @@ public class AccessibilityShortcutController { Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED, "1" /* Value to enable */, "0" /* Value to disable */, R.string.color_correction_feature_name)); + featuresMap.put(REDUCE_BRIGHT_COLORS_COMPONENT_NAME, + new ToggleableFrameworkFeatureInfo( + Settings.Secure.REDUCE_BRIGHT_COLORS_ACTIVATED, + "1" /* Value to enable */, "0" /* Value to disable */, + R.string.reduce_bright_colors_feature_name)); sFrameworkShortcutFeaturesMap = Collections.unmodifiableMap(featuresMap); } return sFrameworkShortcutFeaturesMap; diff --git a/core/java/com/android/internal/accessibility/dialog/AccessibilityTargetHelper.java b/core/java/com/android/internal/accessibility/dialog/AccessibilityTargetHelper.java index a7c5f6d5e03f..9d06bb92b205 100644 --- a/core/java/com/android/internal/accessibility/dialog/AccessibilityTargetHelper.java +++ b/core/java/com/android/internal/accessibility/dialog/AccessibilityTargetHelper.java @@ -21,6 +21,7 @@ import static android.view.accessibility.AccessibilityManager.ACCESSIBILITY_BUTT import static com.android.internal.accessibility.AccessibilityShortcutController.COLOR_INVERSION_COMPONENT_NAME; import static com.android.internal.accessibility.AccessibilityShortcutController.DALTONIZER_COMPONENT_NAME; import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_CONTROLLER_NAME; +import static com.android.internal.accessibility.AccessibilityShortcutController.REDUCE_BRIGHT_COLORS_COMPONENT_NAME; import static com.android.internal.accessibility.util.AccessibilityUtils.getAccessibilityServiceFragmentType; import static com.android.internal.accessibility.util.ShortcutUtils.isShortcutContained; @@ -112,7 +113,7 @@ public final class AccessibilityTargetHelper { @ShortcutType int shortcutType) { final List<AccessibilityTarget> targets = new ArrayList<>(); targets.addAll(getAccessibilityFilteredTargets(context, shortcutType)); - targets.addAll(getWhiteListingFeatureTargets(context, shortcutType)); + targets.addAll(getAllowListingFeatureTargets(context, shortcutType)); return targets; } @@ -196,12 +197,12 @@ public final class AccessibilityTargetHelper { return targets; } - private static List<AccessibilityTarget> getWhiteListingFeatureTargets(Context context, + private static List<AccessibilityTarget> getAllowListingFeatureTargets(Context context, @ShortcutType int shortcutType) { final List<AccessibilityTarget> targets = new ArrayList<>(); - final InvisibleToggleWhiteListingFeatureTarget magnification = - new InvisibleToggleWhiteListingFeatureTarget(context, + final InvisibleToggleAllowListingFeatureTarget magnification = + new InvisibleToggleAllowListingFeatureTarget(context, shortcutType, isShortcutContained(context, shortcutType, MAGNIFICATION_CONTROLLER_NAME), MAGNIFICATION_CONTROLLER_NAME, @@ -209,8 +210,8 @@ public final class AccessibilityTargetHelper { context.getDrawable(R.drawable.ic_accessibility_magnification), Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED); - final ToggleWhiteListingFeatureTarget daltonizer = - new ToggleWhiteListingFeatureTarget(context, + final ToggleAllowListingFeatureTarget daltonizer = + new ToggleAllowListingFeatureTarget(context, shortcutType, isShortcutContained(context, shortcutType, DALTONIZER_COMPONENT_NAME.flattenToString()), @@ -219,8 +220,8 @@ public final class AccessibilityTargetHelper { context.getDrawable(R.drawable.ic_accessibility_color_correction), Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED); - final ToggleWhiteListingFeatureTarget colorInversion = - new ToggleWhiteListingFeatureTarget(context, + final ToggleAllowListingFeatureTarget colorInversion = + new ToggleAllowListingFeatureTarget(context, shortcutType, isShortcutContained(context, shortcutType, COLOR_INVERSION_COMPONENT_NAME.flattenToString()), @@ -229,9 +230,21 @@ public final class AccessibilityTargetHelper { context.getDrawable(R.drawable.ic_accessibility_color_inversion), Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED); + // TODO: Update with shortcut icon + final ToggleAllowListingFeatureTarget reduceBrightColors = + new ToggleAllowListingFeatureTarget(context, + shortcutType, + isShortcutContained(context, shortcutType, + REDUCE_BRIGHT_COLORS_COMPONENT_NAME.flattenToString()), + REDUCE_BRIGHT_COLORS_COMPONENT_NAME.flattenToString(), + context.getString(R.string.reduce_bright_colors_feature_name), + null, + Settings.Secure.REDUCE_BRIGHT_COLORS_ACTIVATED); + targets.add(magnification); targets.add(daltonizer); targets.add(colorInversion); + targets.add(reduceBrightColors); return targets; } diff --git a/core/java/com/android/internal/accessibility/dialog/InvisibleToggleWhiteListingFeatureTarget.java b/core/java/com/android/internal/accessibility/dialog/InvisibleToggleAllowListingFeatureTarget.java index acd101bf28ba..e78036d9f1e9 100644 --- a/core/java/com/android/internal/accessibility/dialog/InvisibleToggleWhiteListingFeatureTarget.java +++ b/core/java/com/android/internal/accessibility/dialog/InvisibleToggleAllowListingFeatureTarget.java @@ -26,9 +26,9 @@ import com.android.internal.accessibility.common.ShortcutConstants.Accessibility * Extension for {@link AccessibilityTarget} with {@link AccessibilityFragmentType#INVISIBLE_TOGGLE} * type. */ -class InvisibleToggleWhiteListingFeatureTarget extends AccessibilityTarget { +class InvisibleToggleAllowListingFeatureTarget extends AccessibilityTarget { - InvisibleToggleWhiteListingFeatureTarget(Context context, @ShortcutType int shortcutType, + InvisibleToggleAllowListingFeatureTarget(Context context, @ShortcutType int shortcutType, boolean isShortcutSwitched, String id, CharSequence label, Drawable icon, String key) { super(context, shortcutType, AccessibilityFragmentType.INVISIBLE_TOGGLE, isShortcutSwitched, id, label, icon, key); diff --git a/core/java/com/android/internal/accessibility/dialog/ToggleWhiteListingFeatureTarget.java b/core/java/com/android/internal/accessibility/dialog/ToggleAllowListingFeatureTarget.java index 5ab9eb84f0e4..38aac708de15 100644 --- a/core/java/com/android/internal/accessibility/dialog/ToggleWhiteListingFeatureTarget.java +++ b/core/java/com/android/internal/accessibility/dialog/ToggleAllowListingFeatureTarget.java @@ -32,9 +32,9 @@ import com.android.internal.accessibility.dialog.TargetAdapter.ViewHolder; * Extension for {@link AccessibilityTarget} with {@link AccessibilityFragmentType#TOGGLE} * type. */ -class ToggleWhiteListingFeatureTarget extends AccessibilityTarget { +class ToggleAllowListingFeatureTarget extends AccessibilityTarget { - ToggleWhiteListingFeatureTarget(Context context, @ShortcutType int shortcutType, + ToggleAllowListingFeatureTarget(Context context, @ShortcutType int shortcutType, boolean isShortcutSwitched, String id, CharSequence label, Drawable icon, String key) { super(context, shortcutType, AccessibilityFragmentType.TOGGLE, isShortcutSwitched, id, label, icon, key); diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java index e0f8a1ad0a1a..cb4f285c7772 100644 --- a/core/java/com/android/internal/app/ChooserActivity.java +++ b/core/java/com/android/internal/app/ChooserActivity.java @@ -1655,18 +1655,24 @@ public class ChooserActivity extends ResolverActivity implements private void showTargetDetails(DisplayResolveInfo ti) { if (ti == null) return; - List<DisplayResolveInfo> targetList; + ArrayList<DisplayResolveInfo> targetList; // For multiple targets, include info on all targets if (ti instanceof MultiDisplayResolveInfo) { MultiDisplayResolveInfo mti = (MultiDisplayResolveInfo) ti; targetList = mti.getTargets(); } else { - targetList = Collections.singletonList(ti); + targetList = new ArrayList<DisplayResolveInfo>(); + targetList.add(ti); } - ChooserTargetActionsDialogFragment f = new ChooserTargetActionsDialogFragment( - targetList, mChooserMultiProfilePagerAdapter.getCurrentUserHandle()); + ChooserTargetActionsDialogFragment f = new ChooserTargetActionsDialogFragment(); + Bundle b = new Bundle(); + b.putParcelable(ChooserTargetActionsDialogFragment.USER_HANDLE_KEY, + mChooserMultiProfilePagerAdapter.getCurrentUserHandle()); + b.putParcelableArrayList(ChooserTargetActionsDialogFragment.TARGET_INFOS_KEY, + targetList); + f.setArguments(b); f.show(getFragmentManager(), TARGET_DETAILS_FRAGMENT_TAG); } @@ -1725,9 +1731,14 @@ public class ChooserActivity extends ResolverActivity implements if (targetInfo instanceof MultiDisplayResolveInfo) { MultiDisplayResolveInfo mti = (MultiDisplayResolveInfo) targetInfo; if (!mti.hasSelected()) { - ChooserStackedAppDialogFragment f = new ChooserStackedAppDialogFragment( - mti, which, + ChooserStackedAppDialogFragment f = new ChooserStackedAppDialogFragment(); + Bundle b = new Bundle(); + b.putParcelable(ChooserTargetActionsDialogFragment.USER_HANDLE_KEY, mChooserMultiProfilePagerAdapter.getCurrentUserHandle()); + b.putObject(ChooserStackedAppDialogFragment.MULTI_DRI_KEY, + mti); + b.putInt(ChooserStackedAppDialogFragment.WHICH_KEY, which); + f.setArguments(b); f.show(getFragmentManager(), TARGET_DETAILS_FRAGMENT_TAG); return; diff --git a/core/java/com/android/internal/app/ChooserStackedAppDialogFragment.java b/core/java/com/android/internal/app/ChooserStackedAppDialogFragment.java index fdeba8f67cc1..726622b6e768 100644 --- a/core/java/com/android/internal/app/ChooserStackedAppDialogFragment.java +++ b/core/java/com/android/internal/app/ChooserStackedAppDialogFragment.java @@ -20,6 +20,7 @@ package com.android.internal.app; import android.content.DialogInterface; import android.content.pm.PackageManager; import android.graphics.drawable.Drawable; +import android.os.Bundle; import android.os.UserHandle; import com.android.internal.app.chooser.DisplayResolveInfo; @@ -32,17 +33,26 @@ import com.android.internal.app.chooser.MultiDisplayResolveInfo; public class ChooserStackedAppDialogFragment extends ChooserTargetActionsDialogFragment implements DialogInterface.OnClickListener { + static final String WHICH_KEY = "which_key"; + static final String MULTI_DRI_KEY = "multi_dri_key"; + private MultiDisplayResolveInfo mMultiDisplayResolveInfo; private int mParentWhich; - public ChooserStackedAppDialogFragment() { + public ChooserStackedAppDialogFragment() {} + + void setStateFromBundle(Bundle b) { + mMultiDisplayResolveInfo = (MultiDisplayResolveInfo) b.get(MULTI_DRI_KEY); + mTargetInfos = mMultiDisplayResolveInfo.getTargets(); + mUserHandle = (UserHandle) b.get(USER_HANDLE_KEY); + mParentWhich = b.getInt(WHICH_KEY); } - public ChooserStackedAppDialogFragment(MultiDisplayResolveInfo targets, - int parentWhich, UserHandle userHandle) { - super(targets.getTargets(), userHandle); - mMultiDisplayResolveInfo = targets; - mParentWhich = parentWhich; + @Override + public void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + outState.putInt(WHICH_KEY, mParentWhich); + outState.putParcelable(MULTI_DRI_KEY, mMultiDisplayResolveInfo); } @Override @@ -53,6 +63,7 @@ public class ChooserStackedAppDialogFragment extends ChooserTargetActionsDialogF @Override protected Drawable getItemIcon(DisplayResolveInfo dri) { + // Show no icon for the group disambig dialog, null hides the imageview return null; } diff --git a/core/java/com/android/internal/app/ChooserTargetActionsDialogFragment.java b/core/java/com/android/internal/app/ChooserTargetActionsDialogFragment.java index 21063d5d5857..9afc0e9e97b0 100644 --- a/core/java/com/android/internal/app/ChooserTargetActionsDialogFragment.java +++ b/core/java/com/android/internal/app/ChooserTargetActionsDialogFragment.java @@ -32,7 +32,6 @@ import android.content.ComponentName; import android.content.DialogInterface; import android.content.SharedPreferences; import android.content.pm.PackageManager; -import android.content.res.Configuration; import android.graphics.Color; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; @@ -59,25 +58,51 @@ import java.util.Optional; public class ChooserTargetActionsDialogFragment extends DialogFragment implements DialogInterface.OnClickListener { - protected List<DisplayResolveInfo> mTargetInfos = new ArrayList<>(); + protected ArrayList<DisplayResolveInfo> mTargetInfos = new ArrayList<>(); protected UserHandle mUserHandle; - public ChooserTargetActionsDialogFragment() { + public static final String USER_HANDLE_KEY = "user_handle"; + public static final String TARGET_INFOS_KEY = "target_infos"; + + public ChooserTargetActionsDialogFragment() {} + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + if (savedInstanceState != null) { + setStateFromBundle(savedInstanceState); + } else { + setStateFromBundle(getArguments()); + } } - public ChooserTargetActionsDialogFragment(List<DisplayResolveInfo> targets, - UserHandle userHandle) { - mUserHandle = userHandle; - mTargetInfos = targets; + void setStateFromBundle(Bundle b) { + mTargetInfos = (ArrayList<DisplayResolveInfo>) b.get(TARGET_INFOS_KEY); + mUserHandle = (UserHandle) b.get(USER_HANDLE_KEY); + } + + @Override + public void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + + outState.putParcelable(ChooserTargetActionsDialogFragment.USER_HANDLE_KEY, + mUserHandle); + outState.putParcelableArrayList(ChooserTargetActionsDialogFragment.TARGET_INFOS_KEY, + mTargetInfos); } /** * Recreate the layout from scratch to match new Sharesheet redlines */ + @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) { - + if (savedInstanceState != null) { + setStateFromBundle(savedInstanceState); + } else { + setStateFromBundle(getArguments()); + } // Make the background transparent to show dialog rounding Optional.of(getDialog()).map(Dialog::getWindow) .ifPresent(window -> { @@ -204,12 +229,4 @@ public class ChooserTargetActionsDialogFragment extends DialogFragment mTargetInfos.get(0).getResolveInfo()); } - @Override - public void onConfigurationChanged(Configuration newConfig) { - // Dismiss on config changed (eg: rotation) - // TODO: Maintain state on config change - super.onConfigurationChanged(newConfig); - dismiss(); - } - } diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java index 6edf7a3204d4..83cbe38f6cb4 100644 --- a/core/java/com/android/internal/app/ResolverActivity.java +++ b/core/java/com/android/internal/app/ResolverActivity.java @@ -1291,7 +1291,7 @@ public class ResolverActivity extends Activity implements private void safelyStartActivityInternal(TargetInfo cti) { // If the target is suspended, the activity will not be successfully launched. // Do not unregister from package manager updates in this case - if (!cti.isSuspended()) { + if (!cti.isSuspended() && mRegistered) { if (mPersonalPackageMonitor != null) { mPersonalPackageMonitor.unregister(); } diff --git a/core/java/com/android/internal/app/chooser/DisplayResolveInfo.java b/core/java/com/android/internal/app/chooser/DisplayResolveInfo.java index fe0e7d012262..b00148af312f 100644 --- a/core/java/com/android/internal/app/chooser/DisplayResolveInfo.java +++ b/core/java/com/android/internal/app/chooser/DisplayResolveInfo.java @@ -27,19 +27,22 @@ import android.content.pm.ApplicationInfo; import android.content.pm.ResolveInfo; import android.graphics.drawable.Drawable; import android.os.Bundle; +import android.os.Parcel; +import android.os.Parcelable; import android.os.UserHandle; import com.android.internal.app.ResolverActivity; import com.android.internal.app.ResolverListAdapter.ResolveInfoPresentationGetter; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; /** * A TargetInfo plus additional information needed to render it (such as icon and label) and * resolve it to an activity. */ -public class DisplayResolveInfo implements TargetInfo { +public class DisplayResolveInfo implements TargetInfo, Parcelable { // Temporary flag for new chooser delegate behavior. There are occassional token // permission errors from bouncing through the delegate. Watch out before reenabling: // b/157272342 is one example but this issue has been reported many times @@ -202,4 +205,41 @@ public class DisplayResolveInfo implements TargetInfo { mPinned = pinned; } + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeCharSequence(mDisplayLabel); + dest.writeCharSequence(mExtendedInfo); + dest.writeParcelable(mResolvedIntent, 0); + dest.writeParcelableArray((Intent[]) mSourceIntents.toArray(), 0); + dest.writeBoolean(mIsSuspended); + dest.writeBoolean(mPinned); + dest.writeParcelable(mResolveInfo, 0); + } + + public static final Parcelable.Creator<DisplayResolveInfo> CREATOR = + new Parcelable.Creator<DisplayResolveInfo>() { + public DisplayResolveInfo createFromParcel(Parcel in) { + return new DisplayResolveInfo(in); + } + + public DisplayResolveInfo[] newArray(int size) { + return new DisplayResolveInfo[size]; + } + }; + + private DisplayResolveInfo(Parcel in) { + mDisplayLabel = in.readCharSequence(); + mExtendedInfo = in.readCharSequence(); + mResolvedIntent = in.readParcelable(null /* ClassLoader */); + mSourceIntents.addAll( + Arrays.asList((Intent[]) in.readParcelableArray(null /* ClassLoader */))); + mIsSuspended = in.readBoolean(); + mPinned = in.readBoolean(); + mResolveInfo = in.readParcelable(null /* ClassLoader */); + } } diff --git a/core/java/com/android/internal/app/chooser/MultiDisplayResolveInfo.java b/core/java/com/android/internal/app/chooser/MultiDisplayResolveInfo.java index cf921d734d48..2828e254a665 100644 --- a/core/java/com/android/internal/app/chooser/MultiDisplayResolveInfo.java +++ b/core/java/com/android/internal/app/chooser/MultiDisplayResolveInfo.java @@ -23,14 +23,13 @@ import android.os.UserHandle; import com.android.internal.app.ResolverActivity; import java.util.ArrayList; -import java.util.List; /** * Represents a "stack" of chooser targets for various activities within the same component. */ public class MultiDisplayResolveInfo extends DisplayResolveInfo { - List<DisplayResolveInfo> mTargetInfos = new ArrayList<>(); + ArrayList<DisplayResolveInfo> mTargetInfos = new ArrayList<>(); // We'll use this DRI for basic presentation info - eg icon, name. final DisplayResolveInfo mBaseInfo; // Index of selected target @@ -61,7 +60,7 @@ public class MultiDisplayResolveInfo extends DisplayResolveInfo { /** * List of all DisplayResolveInfos included in this target. */ - public List<DisplayResolveInfo> getTargets() { + public ArrayList<DisplayResolveInfo> getTargets() { return mTargetInfos; } diff --git a/core/java/com/android/internal/compat/CompatibilityChangeInfo.java b/core/java/com/android/internal/compat/CompatibilityChangeInfo.java index 9ba025988126..670ca9f6091e 100644 --- a/core/java/com/android/internal/compat/CompatibilityChangeInfo.java +++ b/core/java/com/android/internal/compat/CompatibilityChangeInfo.java @@ -28,7 +28,7 @@ import android.os.Parcelable; public class CompatibilityChangeInfo implements Parcelable { private final long mChangeId; private final @Nullable String mName; - private final int mEnableAfterTargetSdk; + private final int mEnableSinceTargetSdk; private final boolean mDisabled; private final boolean mLoggingOnly; private final @Nullable String mDescription; @@ -42,8 +42,8 @@ public class CompatibilityChangeInfo implements Parcelable { return mName; } - public int getEnableAfterTargetSdk() { - return mEnableAfterTargetSdk; + public int getEnableSinceTargetSdk() { + return mEnableSinceTargetSdk; } public boolean getDisabled() { @@ -59,20 +59,37 @@ public class CompatibilityChangeInfo implements Parcelable { } public CompatibilityChangeInfo( - Long changeId, String name, int enableAfterTargetSdk, boolean disabled, - boolean loggingOnly, String description) { + Long changeId, String name, int enableAfterTargetSdk, int enableSinceTargetSdk, + boolean disabled, boolean loggingOnly, String description) { this.mChangeId = changeId; this.mName = name; - this.mEnableAfterTargetSdk = enableAfterTargetSdk; + if (enableAfterTargetSdk > 0) { + // Need to maintain support for @EnabledAfter(X), but make it equivalent to + // @EnabledSince(X+1) + this.mEnableSinceTargetSdk = enableAfterTargetSdk + 1; + } else if (enableSinceTargetSdk > 0) { + this.mEnableSinceTargetSdk = enableSinceTargetSdk; + } else { + this.mEnableSinceTargetSdk = -1; + } this.mDisabled = disabled; this.mLoggingOnly = loggingOnly; this.mDescription = description; } + public CompatibilityChangeInfo(CompatibilityChangeInfo other) { + this.mChangeId = other.mChangeId; + this.mName = other.mName; + this.mEnableSinceTargetSdk = other.mEnableSinceTargetSdk; + this.mDisabled = other.mDisabled; + this.mLoggingOnly = other.mLoggingOnly; + this.mDescription = other.mDescription; + } + private CompatibilityChangeInfo(Parcel in) { mChangeId = in.readLong(); mName = in.readString(); - mEnableAfterTargetSdk = in.readInt(); + mEnableSinceTargetSdk = in.readInt(); mDisabled = in.readBoolean(); mLoggingOnly = in.readBoolean(); mDescription = in.readString(); @@ -87,7 +104,7 @@ public class CompatibilityChangeInfo implements Parcelable { public void writeToParcel(Parcel dest, int flags) { dest.writeLong(mChangeId); dest.writeString(mName); - dest.writeInt(mEnableAfterTargetSdk); + dest.writeInt(mEnableSinceTargetSdk); dest.writeBoolean(mDisabled); dest.writeBoolean(mLoggingOnly); dest.writeString(mDescription); @@ -100,8 +117,8 @@ public class CompatibilityChangeInfo implements Parcelable { if (getName() != null) { sb.append("; name=").append(getName()); } - if (getEnableAfterTargetSdk() != -1) { - sb.append("; enableAfterTargetSdk=").append(getEnableAfterTargetSdk()); + if (getEnableSinceTargetSdk() != -1) { + sb.append("; enableSinceTargetSdk=").append(getEnableSinceTargetSdk()); } if (getDisabled()) { sb.append("; disabled"); @@ -123,7 +140,7 @@ public class CompatibilityChangeInfo implements Parcelable { CompatibilityChangeInfo that = (CompatibilityChangeInfo) o; return this.mChangeId == that.mChangeId && this.mName.equals(that.mName) - && this.mEnableAfterTargetSdk == that.mEnableAfterTargetSdk + && this.mEnableSinceTargetSdk == that.mEnableSinceTargetSdk && this.mDisabled == that.mDisabled && this.mLoggingOnly == that.mLoggingOnly && this.mDescription.equals(that.mDescription); diff --git a/core/java/com/android/internal/compat/IPlatformCompat.aidl b/core/java/com/android/internal/compat/IPlatformCompat.aidl index 6408def7eeac..cc266d60465e 100644 --- a/core/java/com/android/internal/compat/IPlatformCompat.aidl +++ b/core/java/com/android/internal/compat/IPlatformCompat.aidl @@ -164,7 +164,7 @@ interface IPlatformCompat boolean clearOverride(long changeId, String packageName); /** - * Enable all compatibility changes which have enabledAfterTargetSdk == + * Enable all compatibility changes which have enabledSinceTargetSdk == * {@param targetSdkVersion} for an app, subject to the policy. Kills the app to allow the * changes to take effect. * diff --git a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java index d23ea3c46695..a2af4d6cf456 100644 --- a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java +++ b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java @@ -132,6 +132,11 @@ public final class SystemUiDeviceConfigFlags { */ public static final String PROPERTY_MIC_CAMERA_ENABLED = "camera_mic_icons_enabled"; + /** + * Whether to show app ops chip for location. + */ + public static final String PROPERTY_LOCATION_INDICATORS_ENABLED = "location_indicators_enabled"; + // Flags related to Assistant /** diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index 17323ba2f16b..b986463a62f7 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -65,7 +65,6 @@ import android.provider.Settings; import android.telephony.CellSignalStrength; import android.telephony.DataConnectionRealTimeInfo; import android.telephony.ModemActivityInfo; -import android.telephony.ModemActivityInfo.TransmitPower; import android.telephony.ServiceState; import android.telephony.SignalStrength; import android.telephony.TelephonyManager; @@ -196,7 +195,7 @@ public class BatteryStatsImpl extends BatteryStats { static final int MSG_REPORT_POWER_CHANGE = 2; static final int MSG_REPORT_CHARGING = 3; static final int MSG_REPORT_RESET_STATS = 4; - static final long DELAY_UPDATE_WAKELOCKS = 5*1000; + static final long DELAY_UPDATE_WAKELOCKS = 60 * 1000; private static final double MILLISECONDS_IN_HOUR = 3600 * 1000; private static final long MILLISECONDS_IN_YEAR = 365 * 24 * 3600 * 1000L; @@ -7791,7 +7790,7 @@ public class BatteryStatsImpl extends BatteryStats { public ControllerActivityCounterImpl getOrCreateModemControllerActivityLocked() { if (mModemControllerActivity == null) { mModemControllerActivity = new ControllerActivityCounterImpl(mBsi.mOnBatteryTimeBase, - ModemActivityInfo.TX_POWER_LEVELS); + ModemActivityInfo.getNumTxPowerLevels()); } return mModemControllerActivity; } @@ -9257,7 +9256,7 @@ public class BatteryStatsImpl extends BatteryStats { if (in.readInt() != 0) { mModemControllerActivity = new ControllerActivityCounterImpl(mBsi.mOnBatteryTimeBase, - ModemActivityInfo.TX_POWER_LEVELS, in); + ModemActivityInfo.getNumTxPowerLevels(), in); } else { mModemControllerActivity = null; } @@ -10520,7 +10519,7 @@ public class BatteryStatsImpl extends BatteryStats { mBluetoothActivity = new ControllerActivityCounterImpl(mOnBatteryTimeBase, NUM_BT_TX_LEVELS); mModemActivity = new ControllerActivityCounterImpl(mOnBatteryTimeBase, - ModemActivityInfo.TX_POWER_LEVELS); + ModemActivityInfo.getNumTxPowerLevels()); mMobileRadioActiveTimer = new StopwatchTimer(mClocks, null, -400, null, mOnBatteryTimeBase); mMobileRadioActivePerAppTimer = new StopwatchTimer(mClocks, null, -401, null, mOnBatteryTimeBase); @@ -11710,26 +11709,7 @@ public class BatteryStatsImpl extends BatteryStats { } } - private ModemActivityInfo mLastModemActivityInfo = - new ModemActivityInfo(0, 0, 0, new int[0], 0); - - private ModemActivityInfo getDeltaModemActivityInfo(ModemActivityInfo activityInfo) { - if (activityInfo == null) { - return null; - } - int[] txTimeMs = new int[ModemActivityInfo.TX_POWER_LEVELS]; - for (int i = 0; i < ModemActivityInfo.TX_POWER_LEVELS; i++) { - txTimeMs[i] = activityInfo.getTransmitPowerInfo().get(i).getTimeInMillis() - - mLastModemActivityInfo.getTransmitPowerInfo().get(i).getTimeInMillis(); - } - ModemActivityInfo deltaInfo = new ModemActivityInfo(activityInfo.getTimestamp(), - activityInfo.getSleepTimeMillis() - mLastModemActivityInfo.getSleepTimeMillis(), - activityInfo.getIdleTimeMillis() - mLastModemActivityInfo.getIdleTimeMillis(), - txTimeMs, - activityInfo.getReceiveTimeMillis() - mLastModemActivityInfo.getReceiveTimeMillis()); - mLastModemActivityInfo = activityInfo; - return deltaInfo; - } + private ModemActivityInfo mLastModemActivityInfo = null; /** * Distribute Cell radio energy info and network traffic to apps. @@ -11746,7 +11726,9 @@ public class BatteryStatsImpl extends BatteryStats { if (DEBUG_ENERGY) { Slog.d(TAG, "Updating mobile radio stats with " + activityInfo); } - ModemActivityInfo deltaInfo = getDeltaModemActivityInfo(activityInfo); + ModemActivityInfo deltaInfo = mLastModemActivityInfo == null ? activityInfo + : mLastModemActivityInfo.getDelta(activityInfo); + mLastModemActivityInfo = activityInfo; // Add modem tx power to history. addModemTxPowerToHistory(deltaInfo, elapsedRealtimeMs, uptimeMs); @@ -11778,10 +11760,9 @@ public class BatteryStatsImpl extends BatteryStats { mModemActivity.getSleepTimeCounter().addCountLocked( deltaInfo.getSleepTimeMillis()); mModemActivity.getRxTimeCounter().addCountLocked(deltaInfo.getReceiveTimeMillis()); - for (int lvl = 0; lvl < ModemActivityInfo.TX_POWER_LEVELS; lvl++) { + for (int lvl = 0; lvl < ModemActivityInfo.getNumTxPowerLevels(); lvl++) { mModemActivity.getTxTimeCounters()[lvl] - .addCountLocked(deltaInfo.getTransmitPowerInfo() - .get(lvl).getTimeInMillis()); + .addCountLocked(deltaInfo.getTransmitDurationMillisAtPowerLevel(lvl)); } // POWER_MODEM_CONTROLLER_OPERATING_VOLTAGE is measured in mV, so convert to V. @@ -11795,11 +11776,11 @@ public class BatteryStatsImpl extends BatteryStats { mPowerProfile.getAveragePower(PowerProfile.POWER_MODEM_CONTROLLER_IDLE) + deltaInfo.getReceiveTimeMillis() * mPowerProfile.getAveragePower(PowerProfile.POWER_MODEM_CONTROLLER_RX); - List<TransmitPower> txPowerInfo = deltaInfo.getTransmitPowerInfo(); - for (int i = 0; i < Math.min(txPowerInfo.size(), + for (int i = 0; i < Math.min(ModemActivityInfo.getNumTxPowerLevels(), CellSignalStrength.getNumSignalStrengthLevels()); i++) { - energyUsed += txPowerInfo.get(i).getTimeInMillis() * mPowerProfile - .getAveragePower(PowerProfile.POWER_MODEM_CONTROLLER_TX, i); + energyUsed += deltaInfo.getTransmitDurationMillisAtPowerLevel(i) + * mPowerProfile.getAveragePower( + PowerProfile.POWER_MODEM_CONTROLLER_TX, i); } // We store the power drain as mAms. @@ -11894,10 +11875,10 @@ public class BatteryStatsImpl extends BatteryStats { } if (totalTxPackets > 0 && entry.txPackets > 0) { - for (int lvl = 0; lvl < ModemActivityInfo.TX_POWER_LEVELS; lvl++) { - long txMs = - entry.txPackets * deltaInfo.getTransmitPowerInfo() - .get(lvl).getTimeInMillis(); + for (int lvl = 0; lvl < ModemActivityInfo.getNumTxPowerLevels(); + lvl++) { + long txMs = entry.txPackets + * deltaInfo.getTransmitDurationMillisAtPowerLevel(lvl); txMs /= totalTxPackets; activityCounter.getTxTimeCounters()[lvl].addCountLocked(txMs); } @@ -11929,18 +11910,14 @@ public class BatteryStatsImpl extends BatteryStats { if (activityInfo == null) { return; } - List<TransmitPower> txPowerInfo = activityInfo.getTransmitPowerInfo(); - if (txPowerInfo == null || txPowerInfo.size() != ModemActivityInfo.TX_POWER_LEVELS) { - return; - } int levelMaxTimeSpent = 0; - for (int i = 1; i < txPowerInfo.size(); i++) { - if (txPowerInfo.get(i).getTimeInMillis() > txPowerInfo.get(levelMaxTimeSpent) - .getTimeInMillis()) { + for (int i = 1; i < ModemActivityInfo.getNumTxPowerLevels(); i++) { + if (activityInfo.getTransmitDurationMillisAtPowerLevel(i) + > activityInfo.getTransmitDurationMillisAtPowerLevel(levelMaxTimeSpent)) { levelMaxTimeSpent = i; } } - if (levelMaxTimeSpent == ModemActivityInfo.TX_POWER_LEVELS - 1) { + if (levelMaxTimeSpent == ModemActivityInfo.getNumTxPowerLevels() - 1) { mHistoryCur.states2 |= HistoryItem.STATE2_CELLULAR_HIGH_TX_POWER_FLAG; addHistoryRecordLocked(elapsedRealtimeMs, uptimeMs); } @@ -12597,11 +12574,11 @@ public class BatteryStatsImpl extends BatteryStats { // This could happen if the isolated uid mapping was removed before that process // was actually killed. mCpuUidUserSysTimeReader.removeUid(uid); - Slog.d(TAG, "Got readings for an isolated uid with no mapping: " + uid); + if (DEBUG) Slog.d(TAG, "Got readings for an isolated uid: " + uid); return; } if (!mUserInfoProvider.exists(UserHandle.getUserId(uid))) { - Slog.d(TAG, "Got readings for an invalid user's uid " + uid); + if (DEBUG) Slog.d(TAG, "Got readings for an invalid user's uid " + uid); mCpuUidUserSysTimeReader.removeUid(uid); return; } @@ -12706,11 +12683,11 @@ public class BatteryStatsImpl extends BatteryStats { uid = mapUid(uid); if (Process.isIsolated(uid)) { mCpuUidFreqTimeReader.removeUid(uid); - Slog.d(TAG, "Got freq readings for an isolated uid with no mapping: " + uid); + if (DEBUG) Slog.d(TAG, "Got freq readings for an isolated uid: " + uid); return; } if (!mUserInfoProvider.exists(UserHandle.getUserId(uid))) { - Slog.d(TAG, "Got freq readings for an invalid user's uid " + uid); + if (DEBUG) Slog.d(TAG, "Got freq readings for an invalid user's uid " + uid); mCpuUidFreqTimeReader.removeUid(uid); return; } @@ -12820,11 +12797,11 @@ public class BatteryStatsImpl extends BatteryStats { uid = mapUid(uid); if (Process.isIsolated(uid)) { mCpuUidActiveTimeReader.removeUid(uid); - Slog.w(TAG, "Got active times for an isolated uid with no mapping: " + uid); + if (DEBUG) Slog.w(TAG, "Got active times for an isolated uid: " + uid); return; } if (!mUserInfoProvider.exists(UserHandle.getUserId(uid))) { - Slog.w(TAG, "Got active times for an invalid user's uid " + uid); + if (DEBUG) Slog.w(TAG, "Got active times for an invalid user's uid " + uid); mCpuUidActiveTimeReader.removeUid(uid); return; } @@ -12850,11 +12827,11 @@ public class BatteryStatsImpl extends BatteryStats { uid = mapUid(uid); if (Process.isIsolated(uid)) { mCpuUidClusterTimeReader.removeUid(uid); - Slog.w(TAG, "Got cluster times for an isolated uid with no mapping: " + uid); + if (DEBUG) Slog.w(TAG, "Got cluster times for an isolated uid: " + uid); return; } if (!mUserInfoProvider.exists(UserHandle.getUserId(uid))) { - Slog.w(TAG, "Got cluster times for an invalid user's uid " + uid); + if (DEBUG) Slog.w(TAG, "Got cluster times for an invalid user's uid " + uid); mCpuUidClusterTimeReader.removeUid(uid); return; } @@ -13462,7 +13439,7 @@ public class BatteryStatsImpl extends BatteryStats { timeInRxSignalStrengthLevelMs[i] = getPhoneSignalStrengthTime(i, rawRealTimeUs, which) / 1000; } - long[] txTimeMs = new long[Math.min(ModemActivityInfo.TX_POWER_LEVELS, + long[] txTimeMs = new long[Math.min(ModemActivityInfo.getNumTxPowerLevels(), counter.getTxTimeCounters().length)]; long totalTxTimeMs = 0; for (int i = 0; i < txTimeMs.length; i++) { @@ -15601,7 +15578,7 @@ public class BatteryStatsImpl extends BatteryStats { mBluetoothActivity = new ControllerActivityCounterImpl(mOnBatteryTimeBase, NUM_BT_TX_LEVELS, in); mModemActivity = new ControllerActivityCounterImpl(mOnBatteryTimeBase, - ModemActivityInfo.TX_POWER_LEVELS, in); + ModemActivityInfo.getNumTxPowerLevels(), in); mHasWifiReporting = in.readInt() != 0; mHasBluetoothReporting = in.readInt() != 0; mHasModemReporting = in.readInt() != 0; diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java index 50eed27976ad..e848da9b8ee3 100644 --- a/core/java/com/android/internal/policy/PhoneWindow.java +++ b/core/java/com/android/internal/policy/PhoneWindow.java @@ -77,7 +77,7 @@ import android.util.TypedValue; import android.view.ContextThemeWrapper; import android.view.Gravity; import android.view.IRotationWatcher.Stub; -import android.view.IScrollCaptureController; +import android.view.IScrollCaptureCallbacks; import android.view.IWindowManager; import android.view.InputDevice; import android.view.InputEvent; @@ -1511,11 +1511,13 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { if (drawable != mBackgroundDrawable) { mBackgroundDrawable = drawable; if (mDecor != null) { + mDecor.startChanging(); mDecor.setWindowBackground(drawable); if (mBackgroundFallbackDrawable != null) { mDecor.setBackgroundFallback(drawable != null ? null : mBackgroundFallbackDrawable); } + mDecor.finishChanging(); } } } @@ -3910,12 +3912,12 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { /** * System request to begin scroll capture. * - * @param controller the controller to receive responses + * @param callbacks to receive responses * @hide */ @Override - public void requestScrollCapture(IScrollCaptureController controller) { - getViewRootImpl().dispatchScrollCaptureRequest(controller); + public void requestScrollCapture(IScrollCaptureCallbacks callbacks) { + getViewRootImpl().dispatchScrollCaptureRequest(callbacks); } /** @@ -3924,7 +3926,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { * @param callback the callback to add */ @Override - public void addScrollCaptureCallback(@NonNull ScrollCaptureCallback callback) { + public void registerScrollCaptureCallback(@NonNull ScrollCaptureCallback callback) { getViewRootImpl().addScrollCaptureCallback(callback); } @@ -3934,7 +3936,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { * @param callback the callback to remove */ @Override - public void removeScrollCaptureCallback(@NonNull ScrollCaptureCallback callback) { + public void unregisterScrollCaptureCallback(@NonNull ScrollCaptureCallback callback) { getViewRootImpl().removeScrollCaptureCallback(callback); } diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl index caae5188b6a0..77c7ce8cf5c4 100644 --- a/core/java/com/android/internal/statusbar/IStatusBar.aidl +++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl @@ -102,6 +102,13 @@ oneway interface IStatusBar void onCameraLaunchGestureDetected(int source); /** + * Notifies the status bar that the Emergency Action launch gesture has been detected. + * + * TODO(b/169175022) Update method name and docs when feature name is locked. + */ + void onEmergencyActionLaunchGestureDetected(); + + /** * Shows the picture-in-picture menu if an activity is in picture-in-picture mode. */ void showPictureInPictureMenu(); diff --git a/core/java/com/android/internal/util/LatencyTracker.java b/core/java/com/android/internal/util/LatencyTracker.java index 555f62c53387..c7ac1895855b 100644 --- a/core/java/com/android/internal/util/LatencyTracker.java +++ b/core/java/com/android/internal/util/LatencyTracker.java @@ -15,14 +15,22 @@ package com.android.internal.util; import android.content.Context; +import android.database.ContentObserver; +import android.net.Uri; import android.os.Build; import android.os.SystemClock; import android.os.Trace; +import android.os.UserHandle; +import android.provider.Settings; import android.util.EventLog; +import android.util.KeyValueListParser; import android.util.Log; import android.util.SparseLongArray; import com.android.internal.logging.EventLogTags; +import com.android.internal.os.BackgroundThread; + +import java.util.concurrent.ThreadLocalRandom; /** * Class to track various latencies in SystemUI. It then writes the latency to statsd and also @@ -34,6 +42,12 @@ import com.android.internal.logging.EventLogTags; */ public class LatencyTracker { private static final String TAG = "LatencyTracker"; + private static final String SETTINGS_ENABLED_KEY = "enabled"; + private static final String SETTINGS_SAMPLING_INTERVAL_KEY = "sampling_interval"; + /** Default to being enabled on debug builds. */ + private static final boolean DEFAULT_ENABLED = Build.IS_DEBUGGABLE; + /** Default to collecting data for 1/5 of all actions (randomly sampled). */ + private static final int DEFAULT_SAMPLING_INTERVAL = 5; /** * Time it takes until the first frame of the notification panel to be displayed while expanding @@ -76,7 +90,7 @@ public class LatencyTracker { */ public static final int ACTION_FACE_WAKE_AND_UNLOCK = 7; - private static final String[] NAMES = new String[] { + private static final String[] NAMES = new String[]{ "expand panel", "toggle recents", "fingerprint wake-and-unlock", @@ -84,9 +98,9 @@ public class LatencyTracker { "check credential unlocked", "turn on screen", "rotate the screen", - "face wake-and-unlock" }; + "face wake-and-unlock"}; - private static final int[] STATSD_ACTION = new int[] { + private static final int[] STATSD_ACTION = new int[]{ FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_EXPAND_PANEL, FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_TOGGLE_RECENTS, FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_FINGERPRINT_WAKE_AND_UNLOCK, @@ -100,20 +114,59 @@ public class LatencyTracker { private static LatencyTracker sLatencyTracker; private final SparseLongArray mStartRtc = new SparseLongArray(); + private final Context mContext; + private volatile int mSamplingInterval; + private volatile boolean mEnabled; public static LatencyTracker getInstance(Context context) { if (sLatencyTracker == null) { - sLatencyTracker = new LatencyTracker(); + synchronized (LatencyTracker.class) { + if (sLatencyTracker == null) { + sLatencyTracker = new LatencyTracker(context); + } + } } return sLatencyTracker; } + public LatencyTracker(Context context) { + mContext = context; + mEnabled = DEFAULT_ENABLED; + mSamplingInterval = DEFAULT_SAMPLING_INTERVAL; + + // Post initialization to the background in case we're running on the main thread. + BackgroundThread.getHandler().post(this::registerSettingsObserver); + BackgroundThread.getHandler().post(this::readSettings); + } + + private void registerSettingsObserver() { + Uri settingsUri = Settings.Global.getUriFor(Settings.Global.LATENCY_TRACKER); + mContext.getContentResolver().registerContentObserver( + settingsUri, false, new SettingsObserver(this), UserHandle.myUserId()); + } + + private void readSettings() { + KeyValueListParser parser = new KeyValueListParser(','); + String settingsValue = Settings.Global.getString(mContext.getContentResolver(), + Settings.Global.LATENCY_TRACKER); + + try { + parser.setString(settingsValue); + mSamplingInterval = parser.getInt(SETTINGS_SAMPLING_INTERVAL_KEY, + DEFAULT_SAMPLING_INTERVAL); + mEnabled = parser.getBoolean(SETTINGS_ENABLED_KEY, DEFAULT_ENABLED); + } catch (IllegalArgumentException e) { + Log.e(TAG, "Incorrect settings format", e); + mEnabled = false; + } + } + public static boolean isEnabled(Context ctx) { return getInstance(ctx).isEnabled(); } public boolean isEnabled() { - return Build.IS_DEBUGGABLE; + return mEnabled; } /** @@ -145,19 +198,48 @@ public class LatencyTracker { } mStartRtc.delete(action); Trace.asyncTraceEnd(Trace.TRACE_TAG_APP, NAMES[action], 0); - logAction(action, (int)(endRtc - startRtc)); + logAction(action, (int) (endRtc - startRtc)); } /** * Logs an action that has started and ended. This needs to be called from the main thread. * - * @param action The action to end. One of the ACTION_* values. - * @param duration The duration of the action in ms. + * @param action The action to end. One of the ACTION_* values. + * @param duration The duration of the action in ms. + */ + public void logAction(int action, int duration) { + boolean shouldSample = ThreadLocalRandom.current().nextInt() % mSamplingInterval == 0; + logActionDeprecated(action, duration, shouldSample); + } + + /** + * Logs an action that has started and ended. This needs to be called from the main thread. + * + * @param action The action to end. One of the ACTION_* values. + * @param duration The duration of the action in ms. + * @param writeToStatsLog Whether to write the measured latency to FrameworkStatsLog. */ - public static void logAction(int action, int duration) { + public static void logActionDeprecated(int action, int duration, boolean writeToStatsLog) { Log.i(TAG, "action=" + action + " latency=" + duration); EventLog.writeEvent(EventLogTags.SYSUI_LATENCY, action, duration); - FrameworkStatsLog.write( - FrameworkStatsLog.UI_ACTION_LATENCY_REPORTED, STATSD_ACTION[action], duration); + + if (writeToStatsLog) { + FrameworkStatsLog.write( + FrameworkStatsLog.UI_ACTION_LATENCY_REPORTED, STATSD_ACTION[action], duration); + } + } + + private static class SettingsObserver extends ContentObserver { + private final LatencyTracker mThisTracker; + + SettingsObserver(LatencyTracker thisTracker) { + super(BackgroundThread.getHandler()); + mThisTracker = thisTracker; + } + + @Override + public void onChange(boolean selfChange, Uri uri, int userId) { + mThisTracker.readSettings(); + } } } diff --git a/core/java/com/android/internal/util/Preconditions.java b/core/java/com/android/internal/util/Preconditions.java index dae649a903d5..e80e5454f40e 100644 --- a/core/java/com/android/internal/util/Preconditions.java +++ b/core/java/com/android/internal/util/Preconditions.java @@ -31,6 +31,12 @@ import java.util.Objects; */ public class Preconditions { + /** + * Ensures that an expression checking an argument is true. + * + * @param expression the expression to check + * @throws IllegalArgumentException if {@code expression} is false + */ @UnsupportedAppUsage public static void checkArgument(boolean expression) { if (!expression) { @@ -62,8 +68,9 @@ public class Preconditions { * @param messageArgs arguments for {@code messageTemplate} * @throws IllegalArgumentException if {@code expression} is false */ - public static void checkArgument(boolean expression, - final String messageTemplate, + public static void checkArgument( + final boolean expression, + final @NonNull String messageTemplate, final Object... messageArgs) { if (!expression) { throw new IllegalArgumentException(String.format(messageTemplate, messageArgs)); @@ -114,7 +121,9 @@ public class Preconditions { * @throws IllegalArgumentException if {@code string} is empty */ public static @NonNull <T extends CharSequence> T checkStringNotEmpty( - final T string, final String messageTemplate, final Object... messageArgs) { + final T string, + final @NonNull String messageTemplate, + final Object... messageArgs) { if (TextUtils.isEmpty(string)) { throw new IllegalArgumentException(String.format(messageTemplate, messageArgs)); } @@ -160,17 +169,49 @@ public class Preconditions { } /** + * Ensures that an object reference passed as a parameter to the calling + * method is not null. + * + * @param messageTemplate a printf-style message template to use if the check fails; will + * be converted to a string using {@link String#format(String, Object...)} + * @param messageArgs arguments for {@code messageTemplate} + * @throws NullPointerException if {@code reference} is null + */ + public static @NonNull <T> T checkNotNull( + final T reference, + final @NonNull String messageTemplate, + final Object... messageArgs) { + if (reference == null) { + throw new NullPointerException(String.format(messageTemplate, messageArgs)); + } + return reference; + } + + /** * Ensures the truth of an expression involving the state of the calling * instance, but not involving any parameters to the calling method. * * @param expression a boolean expression - * @param message exception message * @throws IllegalStateException if {@code expression} is false */ @UnsupportedAppUsage - public static void checkState(final boolean expression, String message) { + public static void checkState(final boolean expression) { + checkState(expression, null); + } + + /** + * Ensures the truth of an expression involving the state of the calling + * instance, but not involving any parameters to the calling method. + * + * @param expression a boolean expression + * @param errorMessage the exception message to use if the check fails; will + * be converted to a string using {@link String#valueOf(Object)} + * @throws IllegalStateException if {@code expression} is false + */ + @UnsupportedAppUsage + public static void checkState(final boolean expression, String errorMessage) { if (!expression) { - throw new IllegalStateException(message); + throw new IllegalStateException(errorMessage); } } @@ -179,11 +220,18 @@ public class Preconditions { * instance, but not involving any parameters to the calling method. * * @param expression a boolean expression + * @param messageTemplate a printf-style message template to use if the check fails; will + * be converted to a string using {@link String#format(String, Object...)} + * @param messageArgs arguments for {@code messageTemplate} * @throws IllegalStateException if {@code expression} is false */ - @UnsupportedAppUsage - public static void checkState(final boolean expression) { - checkState(expression, null); + public static void checkState( + final boolean expression, + final @NonNull String messageTemplate, + final Object... messageArgs) { + if (!expression) { + throw new IllegalStateException(String.format(messageTemplate, messageArgs)); + } } /** diff --git a/core/java/com/android/internal/view/BaseIWindow.java b/core/java/com/android/internal/view/BaseIWindow.java index fff9ac9e49b7..8962dc3d59da 100644 --- a/core/java/com/android/internal/view/BaseIWindow.java +++ b/core/java/com/android/internal/view/BaseIWindow.java @@ -24,7 +24,7 @@ import android.os.ParcelFileDescriptor; import android.os.RemoteException; import android.util.MergedConfiguration; import android.view.DragEvent; -import android.view.IScrollCaptureController; +import android.view.IScrollCaptureCallbacks; import android.view.IWindow; import android.view.IWindowSession; import android.view.InsetsSourceControl; @@ -162,9 +162,9 @@ public class BaseIWindow extends IWindow.Stub { } @Override - public void requestScrollCapture(IScrollCaptureController controller) { + public void requestScrollCapture(IScrollCaptureCallbacks callbacks) { try { - controller.onClientUnavailable(); + callbacks.onUnavailable(); } catch (RemoteException ex) { // ignore } diff --git a/core/java/com/android/server/SystemConfig.java b/core/java/com/android/server/SystemConfig.java index ed663cfeb613..a761b4c6af91 100644 --- a/core/java/com/android/server/SystemConfig.java +++ b/core/java/com/android/server/SystemConfig.java @@ -250,7 +250,7 @@ public class SystemConfig { // partition that is used to verify if an overlay package fulfills // the 'config_signature' policy by comparing their signatures: // if the overlay package is signed with the same certificate as - // the package declared in 'config-signature' tag, then the + // the package declared in 'overlay-config-signature' tag, then the // overlay package fulfills the 'config_signature' policy. private String mOverlayConfigSignaturePackage; diff --git a/core/jni/android_graphics_BLASTBufferQueue.cpp b/core/jni/android_graphics_BLASTBufferQueue.cpp index a30c37bbd11c..0fc0451c6d4b 100644 --- a/core/jni/android_graphics_BLASTBufferQueue.cpp +++ b/core/jni/android_graphics_BLASTBufferQueue.cpp @@ -55,7 +55,7 @@ static void nativeDestroy(JNIEnv* env, jclass clazz, jlong ptr) { static jobject nativeGetSurface(JNIEnv* env, jclass clazz, jlong ptr) { sp<BLASTBufferQueue> queue = reinterpret_cast<BLASTBufferQueue*>(ptr); - return android_view_Surface_createFromIGraphicBufferProducer(env, queue->getIGraphicBufferProducer()); + return android_view_Surface_createFromSurface(env, queue->getSurface()); } static void nativeSetNextTransaction(JNIEnv* env, jclass clazz, jlong ptr, jlong transactionPtr) { @@ -69,13 +69,19 @@ static void nativeUpdate(JNIEnv*env, jclass clazz, jlong ptr, jlong surfaceContr queue->update(reinterpret_cast<SurfaceControl*>(surfaceControl), width, height); } +static void nativeFlushShadowQueue(JNIEnv* env, jclass clazz, jlong ptr) { + sp<BLASTBufferQueue> queue = reinterpret_cast<BLASTBufferQueue*>(ptr); + queue->flushShadowQueue(); +} + static const JNINativeMethod gMethods[] = { /* name, signature, funcPtr */ {"nativeCreate", "(Ljava/lang/String;JJJZ)J", (void*)nativeCreate}, {"nativeGetSurface", "(J)Landroid/view/Surface;", (void*)nativeGetSurface}, {"nativeDestroy", "(J)V", (void*)nativeDestroy}, {"nativeSetNextTransaction", "(JJ)V", (void*)nativeSetNextTransaction}, - {"nativeUpdate", "(JJJJ)V", (void*)nativeUpdate}}; + {"nativeUpdate", "(JJJJ)V", (void*)nativeUpdate}, + {"nativeFlushShadowQueue", "(J)V", (void*)nativeFlushShadowQueue}}; int register_android_graphics_BLASTBufferQueue(JNIEnv* env) { int res = jniRegisterNativeMethods(env, "android/graphics/BLASTBufferQueue", diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp index e1180384fcc9..32b8fa61c8a0 100644 --- a/core/jni/android_util_Binder.cpp +++ b/core/jni/android_util_Binder.cpp @@ -960,17 +960,8 @@ static jlong android_os_Binder_clearCallingIdentity() return IPCThreadState::self()->clearCallingIdentity(); } -static void android_os_Binder_restoreCallingIdentity(JNIEnv* env, jobject clazz, jlong token) +static void android_os_Binder_restoreCallingIdentity(jlong token) { - // XXX temporary validation check to debug crashes. - int uid = (int)(token>>32); - if (uid > 0 && uid < 999) { - // In Android currently there are no uids in this range. - char buf[128]; - sprintf(buf, "Restoring bad calling ident: 0x%" PRIx64, token); - jniThrowException(env, "java/lang/IllegalStateException", buf); - return; - } IPCThreadState::self()->restoreCallingIdentity(token); } @@ -1064,6 +1055,7 @@ static const JNINativeMethod gBinderMethods[] = { { "isHandlingTransaction", "()Z", (void*)android_os_Binder_isHandlingTransaction }, // @CriticalNative { "clearCallingIdentity", "()J", (void*)android_os_Binder_clearCallingIdentity }, + // @CriticalNative { "restoreCallingIdentity", "(J)V", (void*)android_os_Binder_restoreCallingIdentity }, // @CriticalNative { "setThreadStrictModePolicy", "(I)V", (void*)android_os_Binder_setThreadStrictModePolicy }, diff --git a/core/jni/android_view_InputEventReceiver.cpp b/core/jni/android_view_InputEventReceiver.cpp index 9ed71ac0fb7b..1ea918a900ad 100644 --- a/core/jni/android_view_InputEventReceiver.cpp +++ b/core/jni/android_view_InputEventReceiver.cpp @@ -343,7 +343,7 @@ status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env, jboolean(focusEvent->getHasFocus()), jboolean(focusEvent->getInTouchMode())); finishInputEvent(seq, true /* handled */); - return OK; + continue; } default: diff --git a/core/jni/android_view_Surface.cpp b/core/jni/android_view_Surface.cpp index 6a07cf7bbf40..5c87f1973bed 100644 --- a/core/jni/android_view_Surface.cpp +++ b/core/jni/android_view_Surface.cpp @@ -313,7 +313,7 @@ static jlong nativeGetFromBlastBufferQueue(JNIEnv* env, jclass clazz, jlong nati return nativeObject; } - sp<Surface> surface(new Surface(bufferProducer, true)); + sp<Surface> surface = queue->getSurface(); if (surface != NULL) { surface->incStrong(&sRefBaseOwner); } diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp index 14198559ef9c..a61903dcb7c8 100644 --- a/core/jni/android_view_SurfaceControl.cpp +++ b/core/jni/android_view_SurfaceControl.cpp @@ -1360,15 +1360,6 @@ static void nativeSeverChildren(JNIEnv* env, jclass clazz, jlong transactionObj, transaction->detachChildren(ctrl); } -static void nativeSetOverrideScalingMode(JNIEnv* env, jclass clazz, jlong transactionObj, - jlong nativeObject, - jint scalingMode) { - auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj); - - auto ctrl = reinterpret_cast<SurfaceControl *>(nativeObject); - transaction->setOverrideScalingMode(ctrl, scalingMode); -} - static jobject nativeGetHdrCapabilities(JNIEnv* env, jclass clazz, jobject tokenObject) { sp<IBinder> token(ibinderForJavaObject(env, tokenObject)); if (token == NULL) return NULL; @@ -1694,8 +1685,6 @@ static const JNINativeMethod sSurfaceControlMethods[] = { (void*)nativeReparent }, {"nativeSeverChildren", "(JJ)V", (void*)nativeSeverChildren } , - {"nativeSetOverrideScalingMode", "(JJI)V", - (void*)nativeSetOverrideScalingMode }, {"nativeCaptureDisplay", "(Landroid/view/SurfaceControl$DisplayCaptureArgs;Landroid/view/SurfaceControl$ScreenCaptureListener;)I", (void*)nativeCaptureDisplay }, diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp index 42aab6ad6918..f791cb105b21 100644 --- a/core/jni/com_android_internal_os_Zygote.cpp +++ b/core/jni/com_android_internal_os_Zygote.cpp @@ -80,6 +80,7 @@ #include <bionic/mte.h> #include <bionic/mte_kernel.h> #include <cutils/fs.h> +#include <cutils/memory.h> #include <cutils/multiuser.h> #include <cutils/sockets.h> #include <private/android_filesystem_config.h> @@ -630,6 +631,13 @@ static void PreApplicationInit() { // Set the jemalloc decay time to 1. mallopt(M_DECAY_TIME, 1); + + // Avoid potentially expensive memory mitigations, mostly meant for system + // processes, in apps. These may cause app compat problems, use more memory, + // or reduce performance. While it would be nice to have them for apps, + // we will have to wait until they are proven out, have more efficient + // hardware, and/or apply them only to new applications. + process_disable_memory_mitigations(); } static void SetUpSeccompFilter(uid_t uid, bool is_child_zygote) { diff --git a/core/jni/core_jni_helpers.h b/core/jni/core_jni_helpers.h index 013c65faa241..526804998896 100644 --- a/core/jni/core_jni_helpers.h +++ b/core/jni/core_jni_helpers.h @@ -47,7 +47,7 @@ static inline jclass FindClassOrDie(JNIEnv* env, const char* class_name) { static inline jfieldID GetFieldIDOrDie(JNIEnv* env, jclass clazz, const char* field_name, const char* field_signature) { jfieldID res = env->GetFieldID(clazz, field_name, field_signature); - LOG_ALWAYS_FATAL_IF(res == NULL, "Unable to find static field %s with signature %s", field_name, + LOG_ALWAYS_FATAL_IF(res == NULL, "Unable to find field %s with signature %s", field_name, field_signature); return res; } diff --git a/core/proto/android/app/settings_enums.proto b/core/proto/android/app/settings_enums.proto index 7fac615aa88d..99fe1afc9c9d 100644 --- a/core/proto/android/app/settings_enums.proto +++ b/core/proto/android/app/settings_enums.proto @@ -2751,4 +2751,14 @@ enum PageId { // OS: R QPR2 BLUETOOTH_PAIRING_RECEIVER = 1851; + + // OPEN: Settings > Display > Screen timeout + // CATEGORY: SETTINGS + // OS: S + SCREEN_TIMEOUT = 1852; + + // OPEN: Settings > Accessibility > Reduce Bright Colors + // CATEGORY: SETTINGS + // OS: S + REDUCE_BRIGHT_COLORS_SETTINGS = 1853; } diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto index d315ff27ef9a..0affcea99706 100644 --- a/core/proto/android/server/windowmanagerservice.proto +++ b/core/proto/android/server/windowmanagerservice.proto @@ -48,8 +48,8 @@ message WindowManagerServiceDumpProto { optional string focused_app = 4; optional IdentifierProto input_method_window = 5; optional bool display_frozen = 6; - optional int32 rotation = 7 [(.android.typedef) = "android.view.Surface.Rotation"]; - optional int32 last_orientation = 8 [(.android.typedef) = "android.content.pm.ActivityInfo.ScreenOrientation"]; + optional int32 rotation = 7 [(.android.typedef) = "android.view.Surface.Rotation", deprecated=true]; + optional int32 last_orientation = 8 [(.android.typedef) = "android.content.pm.ActivityInfo.ScreenOrientation", deprecated=true]; optional int32 focused_display_id = 9; optional bool hard_keyboard_available = 10; } @@ -183,7 +183,7 @@ message DisplayContentProto { repeated WindowTokenProto ime_windows = 8 [deprecated=true]; optional int32 dpi = 9; optional .android.view.DisplayInfoProto display_info = 10; - optional int32 rotation = 11 [(.android.typedef) = "android.view.Surface.Rotation"]; + optional int32 rotation = 11 [(.android.typedef) = "android.view.Surface.Rotation", deprecated=true]; optional ScreenRotationAnimationProto screen_rotation_animation = 12; optional DisplayFramesProto display_frames = 13; optional int32 surface_size = 14 [deprecated=true]; @@ -207,6 +207,8 @@ message DisplayContentProto { optional WindowStateProto current_focus = 30; optional ImeInsetsSourceProviderProto ime_insets_source_provider = 31; optional bool can_show_ime = 32; + + optional DisplayRotationProto display_rotation = 33; } /* represents DisplayArea object */ @@ -242,6 +244,16 @@ message DisplayFramesProto { optional .android.graphics.RectProto current = 3; } +message DisplayRotationProto { + option (.android.msg_privacy).dest = DEST_AUTOMATIC; + + optional int32 rotation = 1 [(.android.typedef) = "android.view.Surface.Rotation"]; + optional bool frozen_to_user_rotation = 2; + optional int32 user_rotation = 3 [(.android.typedef) = "android.view.Surface.Rotation"]; + optional int32 fixed_to_user_rotation_mode = 4; + optional int32 last_orientation = 5 [(.android.typedef) = "android.content.pm.ActivityInfo.ScreenOrientation"]; +} + /* represents DockedStackDividerController */ message DockedStackDividerControllerProto { option (.android.msg_privacy).dest = DEST_AUTOMATIC; @@ -529,4 +541,4 @@ message ImeInsetsSourceProviderProto { optional InsetsSourceProviderProto insets_source_provider = 1; optional WindowStateProto ime_target_from_ime = 2; optional bool is_ime_layout_drawn = 3; -}
\ No newline at end of file +} diff --git a/core/proto/android/telephony/enums.proto b/core/proto/android/telephony/enums.proto index d2c0ed4e4736..d2c02769aa78 100644 --- a/core/proto/android/telephony/enums.proto +++ b/core/proto/android/telephony/enums.proto @@ -91,6 +91,14 @@ enum NetworkTypeEnum { NETWORK_TYPE_NR = 20; } +// Roaming type enums, see android.telephony.ServiceState.RoamingType for definitions. +enum RoamingTypeEnum { + ROAMING_TYPE_NOT_ROAMING = 0; + ROAMING_TYPE_ROAMING = 1; + ROAMING_TYPE_ROAMING_DOMESTIC = 2; + ROAMING_TYPE_ROAMING_INTERNATIONAL = 3; +} + // Signal strength levels, primarily used by android/telephony/SignalStrength.java. enum SignalStrengthEnum { SIGNAL_STRENGTH_NONE_OR_UNKNOWN = 0; @@ -220,3 +228,43 @@ enum SmsSendResultEnum { // Error over IMS, retry on CS SMS_SEND_RESULT_ERROR_FALLBACK = 4; } + +// Data profile of the data call. From +// frameworks/base/telephony/java/com/android/internal/telephony/RILConstants.java +enum DataProfileEnum { + DATA_PROFILE_INVALID = -1; + DATA_PROFILE_DEFAULT = 0; + DATA_PROFILE_TETHERED = 1; + DATA_PROFILE_IMS = 2; + DATA_PROFILE_FOTA = 3; + DATA_PROFILE_CBS = 4; + DATA_PROFILE_OEM_BASE = 1000; +} + +// Reason of data call deactivation. From +// frameworks/base/telephony/java/android/telephony/data/DataService.java#DeactivateDataReason +enum DataDeactivateReasonEnum { + DEACTIVATE_REASON_UNKNOWN = 0; + DEACTIVATE_REASON_NORMAL = 1; + DEACTIVATE_REASON_RADIO_OFF = 2; + DEACTIVATE_REASON_HANDOVER = 3; +} + +// IP type of the data call +// see frameworks/base/telephony/java/android/telephony/data/ApnSetting.java#ProtocolType +enum ApnProtocolEnum { + APN_PROTOCOL_IPV4 = 0; + APN_PROTOCOL_IPV6 = 1; + APN_PROTOCOL_IPV4V6 = 2; + APN_PROTOCOL_PPP = 3; +} + +// Action taken to recover a data call that is stalled. From +// frameworks/opt/telephony/src/java/com/android/internal/telephony/dataconnection/DcTracker.java +// #RecoveryAction +enum DataStallRecoveryActionEnum { + RECOVERY_ACTION_GET_DATA_CALL_LIST = 0; + RECOVERY_ACTION_CLEANUP = 1; + RECOVERY_ACTION_REREGISTER = 2; + RECOVERY_ACTION_RADIO_RESTART = 3; +}
\ No newline at end of file diff --git a/core/proto/android/view/enums.proto b/core/proto/android/view/enums.proto index 0172e7899a18..a601abee17f6 100644 --- a/core/proto/android/view/enums.proto +++ b/core/proto/android/view/enums.proto @@ -60,7 +60,7 @@ enum TransitionTypeEnum { TRANSIT_TASK_OPEN_BEHIND = 16; TRANSIT_TASK_IN_PLACE = 17; TRANSIT_ACTIVITY_RELAUNCH = 18; - TRANSIT_DOCK_TASK_FROM_RECENTS = 19; + TRANSIT_DOCK_TASK_FROM_RECENTS = 19 [deprecated=true]; TRANSIT_KEYGUARD_GOING_AWAY = 20; TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER = 21; TRANSIT_KEYGUARD_OCCLUDE = 22; diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index e2f4f2f9294b..01954510ee08 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -2539,8 +2539,7 @@ <permission android:name="android.permission.START_ANY_ACTIVITY" android:protectionLevel="signature" /> - <!-- Allows an application to start activities from background - @hide --> + <!-- @SystemApi @hide Allows an application to start activities from background --> <permission android:name="android.permission.START_ACTIVITIES_FROM_BACKGROUND" android:protectionLevel="signature|privileged|vendorPrivileged|oem|verifier" /> @@ -4518,6 +4517,12 @@ <permission android:name="android.permission.MANAGE_NOTIFICATIONS" android:protectionLevel="signature" /> + <!-- @SystemApi @TestApi Allows adding/removing enabled notification listener components. + @hide --> + <permission android:name="android.permission.MANAGE_NOTIFICATION_LISTENERS" + android:protectionLevel="signature|installer" /> + <uses-permission android:name="android.permission.MANAGE_NOTIFICATION_LISTENERS" /> + <!-- Allows notifications to be colorized <p>Not for use by third-party applications. @hide --> <permission android:name="android.permission.USE_COLORIZED_NOTIFICATIONS" diff --git a/core/res/res/layout/notification_material_media_transfer_action.xml b/core/res/res/layout/notification_material_media_transfer_action.xml deleted file mode 100644 index 98d8f1eee8c9..000000000000 --- a/core/res/res/layout/notification_material_media_transfer_action.xml +++ /dev/null @@ -1,39 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - ~ Copyright (C) 2019 The Android Open Source Project - ~ - ~ Licensed under the Apache License, Version 2.0 (the "License"); - ~ you may not use this file except in compliance with the License. - ~ You may obtain a copy of the License at - ~ - ~ http://www.apache.org/licenses/LICENSE-2.0 - ~ - ~ Unless required by applicable law or agreed to in writing, software - ~ distributed under the License is distributed on an "AS IS" BASIS, - ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - ~ See the License for the specific language governing permissions and - ~ limitations under the License - --> -<LinearLayout - xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:orientation="horizontal" - android:visibility="gone" - android:padding="4dp" - android:layout_marginStart="10dp" - android:gravity="center" - android:background="@drawable/media_seamless_background"> - <ImageView - android:layout_width="?attr/notificationHeaderIconSize" - android:layout_height="?attr/notificationHeaderIconSize" - android:src="@drawable/ic_media_seamless" - android:id="@+id/media_seamless_image" /> - <TextView - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:textAppearance="?attr/notificationHeaderTextAppearance" - android:text="@string/ext_media_seamless_action" - android:id="@+id/media_seamless_text" - android:paddingEnd="2dp" /> -</LinearLayout>
\ No newline at end of file diff --git a/core/res/res/layout/notification_template_header.xml b/core/res/res/layout/notification_template_header.xml index 9a1b592c895a..88493c9505ed 100644 --- a/core/res/res/layout/notification_template_header.xml +++ b/core/res/res/layout/notification_template_header.xml @@ -160,9 +160,5 @@ android:visibility="gone" android:contentDescription="@string/notification_work_profile_content_description" /> - <include - layout="@layout/notification_material_media_transfer_action" - android:id="@+id/media_seamless" - /> </NotificationHeaderView> diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml index ff8e6ad43252..ac9a597a71b4 100644 --- a/core/res/res/values-de/strings.xml +++ b/core/res/res/values-de/strings.xml @@ -1796,8 +1796,8 @@ <string name="package_updated_device_owner" msgid="7560272363805506941">"Von deinem Administrator aktualisiert"</string> <string name="package_deleted_device_owner" msgid="2292335928930293023">"Von deinem Administrator gelöscht"</string> <string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string> - <string name="battery_saver_description_with_learn_more" msgid="4424488535318105801">"Der Stromsparmodus sorgt für eine längere Akkulaufzeit:\n\n• Das dunkle Design wird aktiviert\n• Hintergrundaktivitäten, einige optische Effekte und weitere Funktionen wie \"Ok Google\" werden abgeschaltet oder eingeschränkt\n\n"<annotation id="url">"Weitere Informationen"</annotation></string> - <string name="battery_saver_description" msgid="6794188153647295212">"Der Stromsparmodus sorgt für eine längere Akkulaufzeit:\n\n• Das dunkle Design wird aktiviert\n• Hintergrundaktivitäten, einige optische Effekte und weitere Funktionen wie \"Ok Google\" werden abgeschaltet oder eingeschränkt"</string> + <string name="battery_saver_description_with_learn_more" msgid="4424488535318105801">"Der Energiesparmodus sorgt für eine längere Akkulaufzeit:\n\n• Das dunkle Design wird aktiviert\n• Hintergrundaktivitäten, einige optische Effekte und weitere Funktionen wie \"Ok Google\" werden abgeschaltet oder eingeschränkt\n\n"<annotation id="url">"Weitere Informationen"</annotation></string> + <string name="battery_saver_description" msgid="6794188153647295212">"Der Energiesparmodus sorgt für eine längere Akkulaufzeit:\n\n• Das dunkle Design wird aktiviert\n• Hintergrundaktivitäten, einige optische Effekte und weitere Funktionen wie \"Ok Google\" werden abgeschaltet oder eingeschränkt"</string> <string name="data_saver_description" msgid="4995164271550590517">"Der Datensparmodus verhindert zum einen, dass manche Apps im Hintergrund Daten senden oder empfangen, sodass weniger Daten verbraucht werden. Zum anderen werden die Datenzugriffe der gerade aktiven App eingeschränkt, was z. B. dazu führen kann, dass Bilder erst angetippt werden müssen, bevor sie sichtbar werden."</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"Datensparmodus aktivieren?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"Aktivieren"</string> @@ -2005,9 +2005,9 @@ <string name="notification_feedback_indicator" msgid="663476517711323016">"Feedback geben"</string> <string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Infomitteilung zum Ablaufmodus"</string> <string name="dynamic_mode_notification_title" msgid="9205715501274608016">"Dein Akku könnte vor der gewöhnlichen Ladezeit leer sein"</string> - <string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"Stromsparmodus aktiviert, um die Akkulaufzeit zu verlängern"</string> - <string name="battery_saver_notification_channel_name" msgid="3918243458067916913">"Stromsparmodus"</string> - <string name="battery_saver_off_notification_title" msgid="7637255960468032515">"Stromsparmodus deaktiviert"</string> + <string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"Energiesparmodus aktiviert, um die Akkulaufzeit zu verlängern"</string> + <string name="battery_saver_notification_channel_name" msgid="3918243458067916913">"Energiesparmodus"</string> + <string name="battery_saver_off_notification_title" msgid="7637255960468032515">"Energiesparmodus deaktiviert"</string> <string name="battery_saver_charged_notification_summary" product="default" msgid="5544457317418624367">"Das Smartphone ist ausreichend geladen. Es sind keine Funktionen mehr beschränkt."</string> <string name="battery_saver_charged_notification_summary" product="tablet" msgid="4426317048139996888">"Das Tablet ist ausreichend geladen. Es sind keine Funktionen mehr beschränkt."</string> <string name="battery_saver_charged_notification_summary" product="device" msgid="1031562417867646649">"Das Gerät ist ausreichend geladen. Es sind keine Funktionen mehr beschränkt."</string> diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml index 907fe5bc20ca..21465542092f 100644 --- a/core/res/res/values-fa/strings.xml +++ b/core/res/res/values-fa/strings.xml @@ -156,9 +156,9 @@ <string name="httpErrorOk" msgid="6206751415788256357">"تأیید"</string> <string name="httpError" msgid="3406003584150566720">"خطایی در شبکه وجود داشت."</string> <string name="httpErrorLookup" msgid="3099834738227549349">"نشانی اینترنتی پیدا نشد."</string> - <string name="httpErrorUnsupportedAuthScheme" msgid="3976195595501606787">"طرح کلی احراز هویت سایت پشتیبانی نمیشود."</string> + <string name="httpErrorUnsupportedAuthScheme" msgid="3976195595501606787">"طرح کلی اصالتسنجی سایت پشتیبانی نمیشود."</string> <string name="httpErrorAuth" msgid="469553140922938968">"راستیآزمایی ناموفق بود."</string> - <string name="httpErrorProxyAuth" msgid="7229662162030113406">"احراز هویت از طریق سرور پروکسی انجام نشد."</string> + <string name="httpErrorProxyAuth" msgid="7229662162030113406">"اصالتسنجی از طریق سرور پروکسی انجام نشد."</string> <string name="httpErrorConnect" msgid="3295081579893205617">"اتصال به سرور انجام نشد."</string> <string name="httpErrorIO" msgid="3860318696166314490">"برقراری ارتباط با سرور ممکن نبود. بعداً دوباره امتحان کنید."</string> <string name="httpErrorTimeout" msgid="7446272815190334204">"زمان اتصال به سرور تمام شده است."</string> @@ -529,11 +529,11 @@ <string name="permlab_requestPasswordComplexity" msgid="1808977190557794109">"درخواست پیچیدگی قفل صفحه"</string> <string name="permdesc_requestPasswordComplexity" msgid="1130556896836258567">"به برنامه اجازه میدهد سطح پیچیدگی قفل صفحه (بالا، متوسط، پایین، یا هیچکدام) را بیاموزد که نشاندهنده بازه ممکن طول و نوع قفل صفحه است. همچنین برنامه میتواند به کاربران پیشنهاد دهد قفل صفحه را به سطح خاصی بهروزرسانی کنند، اما کاربران میتوانند آزادانه این پیشنهاد را نادیده بگیرند و به سطح دیگری بروند. توجه داشته باشید که قفل صفحه در قالب نوشتار ساده ذخیره نمیشود، بنابراین برنامه گذرواژه دقیق را نمیداند."</string> <string name="permlab_useBiometric" msgid="6314741124749633786">"استفاده از سختافزار بیومتریک"</string> - <string name="permdesc_useBiometric" msgid="7502858732677143410">"به برنامه امکان میدهد از سختافزار بیومتریک برای احراز هویت استفاده کند"</string> + <string name="permdesc_useBiometric" msgid="7502858732677143410">"به برنامه امکان میدهد از سختافزار بیومتریک برای اصالتسنجی استفاده کند"</string> <string name="permlab_manageFingerprint" msgid="7432667156322821178">"مدیریت سختافزار اثر انگشت"</string> <string name="permdesc_manageFingerprint" msgid="2025616816437339865">"به برنامه امکان میدهد روشهایی را برای افزودن و حذف الگوهای اثر انگشت جهت استفاده، فعال کند."</string> <string name="permlab_useFingerprint" msgid="1001421069766751922">"استفاده از سختافزار اثر انگشت"</string> - <string name="permdesc_useFingerprint" msgid="412463055059323742">"به برنامه امکان میدهد از سختافزار اثر انگشت برای احراز هویت استفاده کند"</string> + <string name="permdesc_useFingerprint" msgid="412463055059323742">"به برنامه امکان میدهد از سختافزار اثر انگشت برای اصالتسنجی استفاده کند"</string> <string name="permlab_audioWrite" msgid="8501705294265669405">"تغییر مجموعه موسیقی شما"</string> <string name="permdesc_audioWrite" msgid="8057399517013412431">"به برنامه اجازه میدهد مجموعه موسیقیتان را تغییر دهد."</string> <string name="permlab_videoWrite" msgid="5940738769586451318">"تغییر مجموعه ویدیوی شما"</string> @@ -544,11 +544,11 @@ <string name="permdesc_mediaLocation" msgid="597912899423578138">"به برنامه اجازه میدهد مکانها را از مجموعه رسانهتان بخواند."</string> <string name="biometric_dialog_default_title" msgid="55026799173208210">"تأیید کنید این شما هستید"</string> <string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"سختافزار زیستسنجی دردسترس نیست"</string> - <string name="biometric_error_user_canceled" msgid="6732303949695293730">"احراز هویت لغو شد"</string> + <string name="biometric_error_user_canceled" msgid="6732303949695293730">"اصالتسنجی لغو شد"</string> <string name="biometric_not_recognized" msgid="5106687642694635888">"شناسایی نشد"</string> - <string name="biometric_error_canceled" msgid="8266582404844179778">"احراز هویت لغو شد"</string> + <string name="biometric_error_canceled" msgid="8266582404844179778">"اصالتسنجی لغو شد"</string> <string name="biometric_error_device_not_secured" msgid="3129845065043995924">"پین، الگو یا گذرواژهای تنظیم نشده است"</string> - <string name="biometric_error_generic" msgid="6784371929985434439">"خطا هنگام احراز هویت"</string> + <string name="biometric_error_generic" msgid="6784371929985434439">"خطا هنگام اصالتسنجی"</string> <string name="fingerprint_acquired_partial" msgid="8532380671091299342">"بخشی از اثر انگشت شناسایی شد. لطفاً دوباره امتحان کنید."</string> <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"اثرانگشت پردازش نشد. لطفاً دوباره امتحان کنید."</string> <string name="fingerprint_acquired_imager_dirty" msgid="4694800187151533990">"حسگر اثر انگشت کثیف است. لطفاً آن را تمیز کنید و دوباره امتحان نمایید."</string> @@ -556,9 +556,9 @@ <string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"حرکت انگشت خیلی آهسته بود. لطفاً دوباره امتحان کنید."</string> <string-array name="fingerprint_acquired_vendor"> </string-array> - <string name="fingerprint_authenticated" msgid="2024862866860283100">"اثر انگشت احراز هویت شد"</string> - <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"چهره احراز هویت شد"</string> - <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"چهره احراز هویت شد، لطفاً تأیید را فشار دهید"</string> + <string name="fingerprint_authenticated" msgid="2024862866860283100">"اثر انگشت اصالتسنجی شد"</string> + <string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"چهره اصالتسنجی شد"</string> + <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"چهره اصالتسنجی شد، لطفاً تأیید را فشار دهید"</string> <string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"سختافزار اثرانگشت در دسترس نیست."</string> <string name="fingerprint_error_no_space" msgid="6126456006769817485">"ذخیره اثر انگشت ممکن نیست. لطفاً یک اثر انگشت موجود را حذف کنید."</string> <string name="fingerprint_error_timeout" msgid="2946635815726054226">"درنگ ثبت اثر انگشت به پایان رسید. دوباره امتحان کنید."</string> @@ -577,7 +577,7 @@ <string name="permlab_manageFace" msgid="4569549381889283282">"مدیریت سختافزار «بازگشایی با چهره»"</string> <string name="permdesc_manageFace" msgid="6204569688492710471">"به برنامه امکان میدهد روشهایی را برای افزودن و حذف الگوهای چهره جهت استفاده فرابخواند."</string> <string name="permlab_useFaceAuthentication" msgid="1011430526454859030">"استفاده از سختافزار «بازگشایی با چهره»"</string> - <string name="permdesc_useFaceAuthentication" msgid="3115697017684668012">"به برنامه امکان میدهد از سختافزار «بازگشایی با چهره» برای احراز هویت استفاده کند"</string> + <string name="permdesc_useFaceAuthentication" msgid="3115697017684668012">"به برنامه امکان میدهد از سختافزار «بازگشایی با چهره» برای اصالتسنجی استفاده کند"</string> <string name="face_recalibrate_notification_name" msgid="6006095897989257026">"بازگشایی با چهره"</string> <string name="face_recalibrate_notification_title" msgid="5944930528030496897">"ثبت مجدد چهره"</string> <string name="face_recalibrate_notification_content" msgid="892757485125249962">"برای بهبود تشخیص، لطفاً چهرهتان را دوباره ثبت کنید"</string> diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml index 4e2bbe2111b5..6fade981b4e6 100644 --- a/core/res/res/values-hy/strings.xml +++ b/core/res/res/values-hy/strings.xml @@ -1912,7 +1912,7 @@ <string name="app_info" msgid="6113278084877079851">"Հավելվածի մասին"</string> <string name="negative_duration" msgid="1938335096972945232">"−<xliff:g id="TIME">%1$s</xliff:g>"</string> <string name="demo_starting_message" msgid="6577581216125805905">"Ցուցադրական օգտատերը գործարկվում է…"</string> - <string name="demo_restarting_message" msgid="1160053183701746766">"Սարաքը վերակայվում է…"</string> + <string name="demo_restarting_message" msgid="1160053183701746766">"Սարքը վերակայվում է…"</string> <string name="suspended_widget_accessibility" msgid="6331451091851326101">"Անջատած <xliff:g id="LABEL">%1$s</xliff:g>"</string> <string name="conference_call" msgid="5731633152336490471">"Կոնֆերանս զանգ"</string> <string name="tooltip_popup_title" msgid="7863719020269945722">"Հուշակ"</string> diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml index 7057b844da7c..012acf53a3a4 100644 --- a/core/res/res/values-ky/strings.xml +++ b/core/res/res/values-ky/strings.xml @@ -1206,7 +1206,7 @@ <string name="android_upgrading_apk" msgid="1339564803894466737">"<xliff:g id="NUMBER_1">%2$d</xliff:g> ичинен <xliff:g id="NUMBER_0">%1$d</xliff:g> колдонмо ыңгайлаштырылууда."</string> <string name="android_preparing_apk" msgid="589736917792300956">"<xliff:g id="APPNAME">%1$s</xliff:g> даярдалууда."</string> <string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Колдонмолорду иштетип баштоо"</string> - <string name="android_upgrading_complete" msgid="409800058018374746">"Жүктөө аякталууда."</string> + <string name="android_upgrading_complete" msgid="409800058018374746">"Жүктөлүүдө"</string> <string name="heavy_weight_notification" msgid="8382784283600329576">"<xliff:g id="APP">%1$s</xliff:g> иштеп жатат"</string> <string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Оюнга кайтуу үчүн таптаңыз"</string> <string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Оюн тандоо"</string> diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml index c882cb1378e7..6f82395327d8 100644 --- a/core/res/res/values-mk/strings.xml +++ b/core/res/res/values-mk/strings.xml @@ -905,7 +905,7 @@ <string name="keyguard_accessibility_user_selector" msgid="1466067610235696600">"Избирач на корисник"</string> <string name="keyguard_accessibility_status" msgid="6792745049712397237">"Статус"</string> <string name="keyguard_accessibility_camera" msgid="7862557559464986528">"Камера"</string> - <string name="keygaurd_accessibility_media_controls" msgid="2267379779900620614">"Контроли на медиуми"</string> + <string name="keygaurd_accessibility_media_controls" msgid="2267379779900620614">"Контроли за аудио/видео содржини"</string> <string name="keyguard_accessibility_widget_reorder_start" msgid="7066213328912939191">"Прередувањето виџети започна."</string> <string name="keyguard_accessibility_widget_reorder_end" msgid="1083806817600593490">"Прередувањето виџети заврши."</string> <string name="keyguard_accessibility_widget_deleted" msgid="1509738950119878705">"Виџетот <xliff:g id="WIDGET_INDEX">%1$s</xliff:g> е избришан."</string> @@ -1351,7 +1351,7 @@ <string name="ext_media_new_notification_title" product="automotive" msgid="9085349544984742727">"<xliff:g id="NAME">%s</xliff:g> не работи"</string> <string name="ext_media_new_notification_message" msgid="6095403121990786986">"Допрете за поставување"</string> <string name="ext_media_new_notification_message" product="automotive" msgid="5140127881613227162">"Можеби ќе треба да го преформатирате уредот. Допрете за отстранување."</string> - <string name="ext_media_ready_notification_message" msgid="777258143284919261">"За пренесување фотографии и медиуми"</string> + <string name="ext_media_ready_notification_message" msgid="777258143284919261">"За пренесување фотографии и аудио/видео содржини"</string> <string name="ext_media_unmountable_notification_title" msgid="4895444667278979910">"Проблем со <xliff:g id="NAME">%s</xliff:g>"</string> <string name="ext_media_unmountable_notification_title" product="automotive" msgid="3142723758949023280">"<xliff:g id="NAME">%s</xliff:g> не работи"</string> <string name="ext_media_unmountable_notification_message" msgid="3256290114063126205">"Допрете за да го поправите ова"</string> @@ -1392,8 +1392,8 @@ <string name="ext_media_status_formatting" msgid="774148701503179906">"Се форматира..."</string> <string name="ext_media_status_missing" msgid="6520746443048867314">"Не е внесено"</string> <string name="activity_list_empty" msgid="4219430010716034252">"Не се пронајдени соодветни активности."</string> - <string name="permlab_route_media_output" msgid="8048124531439513118">"насочување излез за медиуми"</string> - <string name="permdesc_route_media_output" msgid="1759683269387729675">"Овозможува апликацијата да насочува излез за медиуми кон други надворешни уреди."</string> + <string name="permlab_route_media_output" msgid="8048124531439513118">"насочување излез за аудио/видео"</string> + <string name="permdesc_route_media_output" msgid="1759683269387729675">"Овозможува апликацијата да насочува излез за аудио/видео содржини кон други надворешни уреди."</string> <string name="permlab_readInstallSessions" msgid="7279049337895583621">"читање сесии на инсталирање"</string> <string name="permdesc_readInstallSessions" msgid="4012608316610763473">"Дозволува апликација да чита сесии на инсталирање. Тоа овозможува апликацијата да гледа детали за активни инсталации на пакет."</string> <string name="permlab_requestInstallPackages" msgid="7600020863445351154">"барање пакети за инсталирање"</string> diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml index 141824864086..2183d010283a 100644 --- a/core/res/res/values-mn/strings.xml +++ b/core/res/res/values-mn/strings.xml @@ -957,18 +957,18 @@ <string name="autofill_parish" msgid="6847960518334530198">"Мөргөлч"</string> <string name="autofill_area" msgid="8289022370678448983">"Хэсэг"</string> <string name="autofill_emirate" msgid="2544082046790551168">"Эмират"</string> - <string name="permlab_readHistoryBookmarks" msgid="9102293913842539697">"өөрийн Вэб хавчуурга болон түүхийг унших"</string> + <string name="permlab_readHistoryBookmarks" msgid="9102293913842539697">"өөрийн Веб хавчуурга болон түүхийг унших"</string> <string name="permdesc_readHistoryBookmarks" msgid="2323799501008967852">"Апп нь Хөтчийн зочилж байсан бүх URL-н түүх болон Хөтчийн бүх хавчуургыг унших боломжтой. Анхаар: Энэ зөвшөөрөл нь гуравдагч талын хөтөч эсвэл вебээр хөтөчлөх чадавхтай аппликейшнүүдэд ашиглагдахгүй байх боломжтой."</string> - <string name="permlab_writeHistoryBookmarks" msgid="6090259925187986937">"вэб хавчуурга болон түүхийг бичих"</string> + <string name="permlab_writeHistoryBookmarks" msgid="6090259925187986937">"веб хавчуурга болон түүхийг бичих"</string> <string name="permdesc_writeHistoryBookmarks" product="tablet" msgid="573341025292489065">"Апп нь таны таблет дээр хадгалагдсан Хөтчийн түүх эсвэл хавчуургыг өөрчлөх боломжтой. Энэ нь апп-д Хөтчийн датаг арилгах эсвэл өөрчлөх боломжийг олгоно. Анхаар: Энэ зөвшөөрөл нь гуравдагч талын хөтөч эсвэл вебээр хөтөчлөх чадвартай аппликейшнд ажиллахгүй байх боломжтой."</string> - <string name="permdesc_writeHistoryBookmarks" product="tv" msgid="88642768580408561">"Аппад таны Android TV төхөөрөмжид хадгалсан Хөтчийн түүх эсвэл хавчуургыг өөрчлөхийг зөвшөөрнө. Энэ нь аппад Хөтчийн өгөгдлийг устгах эсвэл өөрчлөхийг зөвшөөрч болзошгүй. Санамж: энэ зөвшөөрөл нь гуравдагч талын хөтөч эсвэл вэб хөтчийн чадамжтай бусад аппад хэрэгжихгүй байж болзошгүй."</string> + <string name="permdesc_writeHistoryBookmarks" product="tv" msgid="88642768580408561">"Аппад таны Android TV төхөөрөмжид хадгалсан Хөтчийн түүх эсвэл хавчуургыг өөрчлөхийг зөвшөөрнө. Энэ нь аппад Хөтчийн өгөгдлийг устгах эсвэл өөрчлөхийг зөвшөөрч болзошгүй. Санамж: энэ зөвшөөрөл нь гуравдагч талын хөтөч эсвэл веб хөтчийн чадамжтай бусад аппад хэрэгжихгүй байж болзошгүй."</string> <string name="permdesc_writeHistoryBookmarks" product="default" msgid="2245203087160913652">"Апп нь таны утсан дээр хадгалагдсан Хөтчийн түүх эсвэл хавчуургыг өөрчлөх боломжтой. Энэ нь апп-д Хөтчийн датаг арилгах эсвэл өөрчлөх боломжийг олгоно. Анхаар: Энэ зөвшөөрөл нь гуравдагч талын хөтөч эсвэл вебээр хөтөчлөх чадвартай аппликейшнд ажиллахгүй байх боломжтой."</string> <string name="permlab_setAlarm" msgid="1158001610254173567">"сэрүүлэг тохируулах"</string> <string name="permdesc_setAlarm" msgid="2185033720060109640">"Апп нь суулгагдсан сэрүүлэгний апп дээр сэрүүлэг тохируулах боломжтой. Зарим сэрүүлэгний апп нь энэ функцийг дэмжихгүй байж болзошгүй."</string> <string name="permlab_addVoicemail" msgid="4770245808840814471">"дуут шуудан нэмэх"</string> <string name="permdesc_addVoicemail" msgid="5470312139820074324">"Таны дуут шуудангийн ирсэн мэйлд зурвас нэмэхийг апп-д зөвшөөрөх."</string> <string name="permlab_writeGeolocationPermissions" msgid="8605631647492879449">"Хөтчийн геобайршлын зөвшөөрлийг өөрчлөх"</string> - <string name="permdesc_writeGeolocationPermissions" msgid="5817346421222227772">"Апп нь Хөтчийн гео байршлын зөвшөөрлийг өөрчлөх боломжтой. Хортой апп нь энийг ашиглан дурын вэб хуудасруу байршлын мэдээллийг илгээх боломжтой."</string> + <string name="permdesc_writeGeolocationPermissions" msgid="5817346421222227772">"Апп нь Хөтчийн гео байршлын зөвшөөрлийг өөрчлөх боломжтой. Хортой апп нь энийг ашиглан дурын веб хуудасруу байршлын мэдээллийг илгээх боломжтой."</string> <string name="save_password_message" msgid="2146409467245462965">"Та хөтчид энэ нууц үгийг сануулах уу?"</string> <string name="save_password_notnow" msgid="2878327088951240061">"Одоо биш"</string> <string name="save_password_remember" msgid="6490888932657708341">"Санах"</string> @@ -1459,7 +1459,7 @@ <string name="progress_erasing" msgid="6891435992721028004">"Хуваалцсан хадгалах санг устгаж байна…"</string> <string name="share" msgid="4157615043345227321">"Хуваалцах"</string> <string name="find" msgid="5015737188624767706">"Олох"</string> - <string name="websearch" msgid="5624340204512793290">"Вэб хайлт"</string> + <string name="websearch" msgid="5624340204512793290">"Веб хайлт"</string> <string name="find_next" msgid="5341217051549648153">"Дараагийнхыг хайх"</string> <string name="find_previous" msgid="4405898398141275532">"Өмнөхөөс олох"</string> <string name="gpsNotifTicker" msgid="3207361857637620780">"<xliff:g id="NAME">%s</xliff:g>-н байршлын хүсэлт"</string> diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml index 3d2fbcb07af9..9b4849f4b19b 100644 --- a/core/res/res/values-mr/strings.xml +++ b/core/res/res/values-mr/strings.xml @@ -197,7 +197,7 @@ <string name="country_detector" msgid="7023275114706088854">"कंट्री डिटेक्टर"</string> <string name="location_service" msgid="2439187616018455546">"स्थान सेवा"</string> <string name="gnss_service" msgid="8907781262179951385">"GNSS सेवा"</string> - <string name="sensor_notification_service" msgid="7474531979178682676">"सेंसर सूचना सेवा"</string> + <string name="sensor_notification_service" msgid="7474531979178682676">"सेन्सर सूचना सेवा"</string> <string name="twilight_service" msgid="8964898045693187224">"ट्वायलाइट सेवा"</string> <string name="factory_reset_warning" msgid="6858705527798047809">"तुमचे डिव्हाइस मिटविले जाईल"</string> <string name="factory_reset_message" msgid="2657049595153992213">"प्रशासक अॅप वापरता येणार नाही. तुमचे डिव्हाइस आता साफ केले जाईल.\n\nतुम्हाला कुठलेही प्रश्न असल्यास, तुमच्या संस्थेच्या प्रशासकाशी संपर्क साधा."</string> @@ -316,7 +316,7 @@ <string name="permgrouplab_phone" msgid="570318944091926620">"फोन"</string> <string name="permgroupdesc_phone" msgid="270048070781478204">"फोन कॉल आणि व्यवस्थापित"</string> <string name="permgrouplab_sensors" msgid="9134046949784064495">"शरीर सेन्सर"</string> - <string name="permgroupdesc_sensors" msgid="2610631290633747752">"आपल्या महत्त्वाच्या मापनांविषयी सेंसर डेटा अॅक्सेस करा"</string> + <string name="permgroupdesc_sensors" msgid="2610631290633747752">"आपल्या महत्त्वाच्या मापनांविषयी सेन्सर डेटा अॅक्सेस करा"</string> <string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"विंडोमधील आशय पुन्हा मिळवा"</string> <string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"तुम्ही वापरत असलेल्या विंडोमधील आशय तपासा."</string> <string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"स्पर्श करून अन्वेषण सुरू करा"</string> @@ -388,7 +388,7 @@ <string name="permlab_getPackageSize" msgid="375391550792886641">"अॅप संचयन स्थान मोजा"</string> <string name="permdesc_getPackageSize" msgid="742743530909966782">"अॅप ला त्याचा कोड, डेटा आणि कॅशे आकार पुनर्प्राप्त करण्यासाठी अनुमती देते"</string> <string name="permlab_writeSettings" msgid="8057285063719277394">"सिस्टम सेटिंग्ज सुधारित करा"</string> - <string name="permdesc_writeSettings" msgid="8293047411196067188">"सिस्टीमचा सेटिंग्ज डेटा सुधारित करण्यासाठी अॅप ला अनुमती देते. दुर्भावनापूर्ण अॅप्स आपल्या सिस्टीमचे कॉन्फिगरेशन दूषित करू शकतात."</string> + <string name="permdesc_writeSettings" msgid="8293047411196067188">"सिस्टीमचा सेटिंग्ज डेटा सुधारित करण्यासाठी अॅप ला अनुमती देते. दुर्भावनापूर्ण अॅप्स आपल्या सिस्टीमचे कॉंफिगरेशन दूषित करू शकतात."</string> <string name="permlab_receiveBootCompleted" msgid="6643339400247325379">"सुरूवातीस चालवा"</string> <string name="permdesc_receiveBootCompleted" product="tablet" msgid="5565659082718177484">"जसे सिस्टम बूट करणे समाप्त करते तसे अॅप ला स्वतः प्रारंभ करण्यास अनुमती देते. यामुळे टॅबलेट प्रारंभ करण्यास वेळ लागू शकतो आणि नेहमी सुरू राहून एकंदर टॅबलेटला धीमे करण्यास अॅप ला अनुमती देते."</string> <string name="permdesc_receiveBootCompleted" product="tv" msgid="4900842256047614307">"सिस्टम बूट होणे संपल्यावर ॲपला स्वतः सुरू होण्याची अनुमती देते. यामुळे तुमच्या Android TV डिव्हाइसला सुरू होण्यास वेळ लागू शकतो आणि नेहमी सुरू राहून एकंदर डिव्हाइसलाच धीमे करण्याची अनुमती ॲपला देते."</string> @@ -411,7 +411,7 @@ <string name="permdesc_writeCallLog" product="tablet" msgid="2657525794731690397">"येणार्या आणि केल्या जाणार्या कॉलविषयीच्या डेटासह, आपल्या टॅब्लेटचा कॉल लॉग सुधारित करण्यासाठी अॅप ला अनुमती देते. दुर्भावनापूर्ण अॅप्स तुमचा कॉल लॉग मिटवण्यासाठी किंवा सुधारित करण्यासाठी याचा वापर करू शकतात."</string> <string name="permdesc_writeCallLog" product="tv" msgid="3934939195095317432">"येणार्या आणि केल्या जाणार्या कॉलविषयीच्या डेटासह, तुमच्या Android TV डिव्हाइसचा कॉल लॉग सुधारित करण्यासाठी ॲपला अनुमती देते. दुर्भावनापूर्ण अॅप्स तुमचा कॉल लॉग मिटवण्यासाठी किंवा सुधारित करण्यासाठी याचा वापर करू शकतात."</string> <string name="permdesc_writeCallLog" product="default" msgid="5903033505665134802">"येणार्या आणि केल्या जाणार्या कॉलविषयीच्या डेटासह, आपल्या फोनचा कॉल लॉग सुधारित करण्यासाठी अॅप ला अनुमती देते. दुर्भावनापूर्ण अॅप्स तुमचा कॉल लॉग मिटवण्यासाठी किंवा सुधारित करण्यासाठी याचा वापर करू शकतात."</string> - <string name="permlab_bodySensors" msgid="3411035315357380862">"शरीर सेंसर (हृदय गती मॉनिटरसारखे) अॅक्सेस करा"</string> + <string name="permlab_bodySensors" msgid="3411035315357380862">"शरीर सेन्सर (हृदय गती मॉनिटरसारखे) अॅक्सेस करा"</string> <string name="permdesc_bodySensors" product="default" msgid="2365357960407973997">"हृदय गती सारख्या, आपल्या शारीरिक स्थितीचे नियंत्रण करणार्या सेन्सरवरून डेटामध्ये प्रवेश करण्यासाठी ॲपला अनुमती देते."</string> <string name="permlab_readCalendar" msgid="6408654259475396200">"कॅलेंडर इव्हेंट आणि तपशील वाचा"</string> <string name="permdesc_readCalendar" product="tablet" msgid="515452384059803326">"हा अॅप आपल्या टॅब्लेटवर स्टोअर केलेले सर्व कॅलेंडर इव्हेंट वाचू आणि शेअर करू शकतो किंवा तुमचा कॅलेंडर डेटा सेव्ह करू शकतो."</string> @@ -565,7 +565,7 @@ <string name="fingerprint_error_canceled" msgid="540026881380070750">"फिंगरप्रिंट ऑपरेशन रद्द झाले."</string> <string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"वापरकर्त्याने फिंगरप्रिंट ऑपरेशन रद्द केले."</string> <string name="fingerprint_error_lockout" msgid="7853461265604738671">"खूप प्रयत्न केले. नंतर पुन्हा प्रयत्न करा."</string> - <string name="fingerprint_error_lockout_permanent" msgid="3895478283943513746">"खूप प्रयत्न करून झाले. फिंगरप्रिंट सेंसर बंद आहे."</string> + <string name="fingerprint_error_lockout_permanent" msgid="3895478283943513746">"खूप प्रयत्न करून झाले. फिंगरप्रिंट सेन्सर बंद आहे."</string> <string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"पुन्हा प्रयत्न करा."</string> <string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"कोणत्याही फिंगरप्रिंटची नोंद झाली नाही"</string> <string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"या डिव्हाइसमध्ये फिंगरप्रिंट सेन्सर नाही."</string> @@ -657,7 +657,7 @@ <string name="permlab_bindDreamService" msgid="4776175992848982706">"स्वप्न सेवेवर प्रतिबद्ध करा"</string> <string name="permdesc_bindDreamService" msgid="9129615743300572973">"होल्डरला स्वप्नसेवेच्या शीर्ष-स्तराच्या इंटरफेसशी प्रतिबद्ध करण्यास अनुमती देते. सामान्य अॅप्सकरिता कधीही आवश्यक नसते."</string> <string name="permlab_invokeCarrierSetup" msgid="5098810760209818140">"वाहकाद्वारे-प्रदान केलेल्या कॉन्फिगरेशन अॅपची विनंती करा"</string> - <string name="permdesc_invokeCarrierSetup" msgid="4790845896063237887">"होल्डरला वाहकद्वारे-प्रदान केलेल्या कॉन्फिगरेशन अॅपची विनंती करण्याची अनुमती देते. सामान्य अॅप्ससाठी कधीही आवश्यक नसावे."</string> + <string name="permdesc_invokeCarrierSetup" msgid="4790845896063237887">"होल्डरला वाहकद्वारे-प्रदान केलेल्या कॉंफिगरेशन अॅपची विनंती करण्याची अनुमती देते. सामान्य अॅप्ससाठी कधीही आवश्यक नसावे."</string> <string name="permlab_accessNetworkConditions" msgid="1270732533356286514">"नेटवर्क स्थितींवरील निरीक्षणांसाठी ऐका"</string> <string name="permdesc_accessNetworkConditions" msgid="2959269186741956109">"अनु्प्रयोगाला नेटवर्क स्थितींवरील निरीक्षणे ऐकण्यासाठी अनुमती देते. सामान्य अॅप्ससाठी कधीही आवश्यक नसावे."</string> <string name="permlab_setInputCalibration" msgid="932069700285223434">"इनपुट डिव्हाइस कॅलिब्रेशन बदला"</string> @@ -673,7 +673,7 @@ <string name="permlab_bindCarrierServices" msgid="2395596978626237474">"वाहक सेवांवर प्रतिबद्ध करा"</string> <string name="permdesc_bindCarrierServices" msgid="9185614481967262900">"वाहक सेवांवर प्रतिबद्ध करण्यासाठी होल्डरला अनुमती देते. सामान्य ॲप्ससाठी कधीही आवश्यकता नसावी."</string> <string name="permlab_access_notification_policy" msgid="5524112842876975537">"व्यत्यय आणू नका अॅक्सेस करा"</string> - <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"व्यत्यय आणू नका कॉन्फिगरेशन वाचण्यासाठी आणि लिहिण्यासाठी ॲपला अनुमती देते."</string> + <string name="permdesc_access_notification_policy" msgid="8538374112403845013">"व्यत्यय आणू नका कॉंफिगरेशन वाचण्यासाठी आणि लिहिण्यासाठी ॲपला अनुमती देते."</string> <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"व्ह्यू परवानगी वापर सुरू करा"</string> <string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"धारकास अॅपसाठी परवानगी वापरणे सुरू करण्याची अनुमती देते. सामान्य अॅप्ससाठी कधीही आवश्यकता नसते."</string> <string name="policylab_limitPassword" msgid="4851829918814422199">"पासवर्ड नियम सेट करा"</string> @@ -752,7 +752,7 @@ </string-array> <string name="phoneTypeCustom" msgid="5120365721260686814">"कस्टम"</string> <string name="phoneTypeHome" msgid="3880132427643623588">"घर"</string> - <string name="phoneTypeMobile" msgid="1178852541462086735">"मोबाईल"</string> + <string name="phoneTypeMobile" msgid="1178852541462086735">"मोबाइल"</string> <string name="phoneTypeWork" msgid="6604967163358864607">"कार्य"</string> <string name="phoneTypeFaxWork" msgid="6757519896109439123">"कार्य फॅक्स"</string> <string name="phoneTypeFaxHome" msgid="6678559953115904345">"निवास फॅक्स"</string> @@ -767,7 +767,7 @@ <string name="phoneTypeRadio" msgid="2637819130239264771">"रेडिओ"</string> <string name="phoneTypeTelex" msgid="2558783611711876562">"टेलेक्स"</string> <string name="phoneTypeTtyTdd" msgid="532038552105328779">"TTY TDD"</string> - <string name="phoneTypeWorkMobile" msgid="7522314392003565121">"कार्य मोबाईल"</string> + <string name="phoneTypeWorkMobile" msgid="7522314392003565121">"कार्य मोबाइल"</string> <string name="phoneTypeWorkPager" msgid="3748332310638505234">"कार्य पेजर"</string> <string name="phoneTypeAssistant" msgid="757550783842231039">"असिस्टंट"</string> <string name="phoneTypeMms" msgid="1799747455131365989">"MMS"</string> @@ -779,7 +779,7 @@ <string name="emailTypeHome" msgid="1597116303154775999">"घर"</string> <string name="emailTypeWork" msgid="2020095414401882111">"कार्य"</string> <string name="emailTypeOther" msgid="5131130857030897465">"अन्य"</string> - <string name="emailTypeMobile" msgid="787155077375364230">"मोबाईल"</string> + <string name="emailTypeMobile" msgid="787155077375364230">"मोबाइल"</string> <string name="postalTypeCustom" msgid="5645590470242939129">"कस्टम"</string> <string name="postalTypeHome" msgid="7562272480949727912">"घर"</string> <string name="postalTypeWork" msgid="8553425424652012826">"कार्य"</string> @@ -1273,8 +1273,8 @@ <string name="sms_control_yes" msgid="4858845109269524622">"अनुमती द्या"</string> <string name="sms_control_no" msgid="4845717880040355570">"नकार द्या"</string> <string name="sms_short_code_confirm_message" msgid="1385416688897538724">"<b><xliff:g id="APP_NAME">%1$s</xliff:g></b> हा <b><xliff:g id="DEST_ADDRESS">%2$s</xliff:g></b>वर एक मेसेज पाठवू इच्छितो."</string> - <string name="sms_short_code_details" msgid="2723725738333388351">"यामुळे आपल्या मोबाईल खात्यावर "<b>"शुल्क आकारले जाऊ शकते"</b>"."</string> - <string name="sms_premium_short_code_details" msgid="1400296309866638111"><b>"यामुळे आपल्या मोबाईल खात्यावर शुल्क आकारले जाऊ शकते."</b></string> + <string name="sms_short_code_details" msgid="2723725738333388351">"यामुळे आपल्या मोबाइल खात्यावर "<b>"शुल्क आकारले जाऊ शकते"</b>"."</string> + <string name="sms_premium_short_code_details" msgid="1400296309866638111"><b>"यामुळे आपल्या मोबाइल खात्यावर शुल्क आकारले जाऊ शकते."</b></string> <string name="sms_short_code_confirm_allow" msgid="920477594325526691">"पाठवा"</string> <string name="sms_short_code_confirm_deny" msgid="1356917469323768230">"रद्द करा"</string> <string name="sms_short_code_remember_choice" msgid="1374526438647744862">"माझी वड लक्षात ठेवा"</string> @@ -1282,10 +1282,10 @@ <string name="sms_short_code_confirm_always_allow" msgid="2223014893129755950">"नेहमी अनुमती द्या"</string> <string name="sms_short_code_confirm_never_allow" msgid="2688828813521652079">"कधीही अनुमती देऊ नका"</string> <string name="sim_removed_title" msgid="5387212933992546283">"सिम कार्ड काढले"</string> - <string name="sim_removed_message" msgid="9051174064474904617">"तुम्ही एक वैध सिम कार्ड घालून प्रारंभ करेपर्यंत मोबाईल नेटवर्क अनुपलब्ध असेल."</string> + <string name="sim_removed_message" msgid="9051174064474904617">"तुम्ही एक वैध सिम कार्ड घालून प्रारंभ करेपर्यंत मोबाइल नेटवर्क अनुपलब्ध असेल."</string> <string name="sim_done_button" msgid="6464250841528410598">"पूर्ण झाले"</string> <string name="sim_added_title" msgid="7930779986759414595">"सिम कार्ड जोडले"</string> - <string name="sim_added_message" msgid="6602906609509958680">"मोबाईल नेटवर्कवर अॅक्सेस करण्यासाठी तुमचे डिव्हाइस रीस्टार्ट करा."</string> + <string name="sim_added_message" msgid="6602906609509958680">"मोबाइल नेटवर्कवर अॅक्सेस करण्यासाठी तुमचे डिव्हाइस रीस्टार्ट करा."</string> <string name="sim_restart_button" msgid="8481803851341190038">"रीस्टार्ट"</string> <string name="install_carrier_app_notification_title" msgid="5712723402213090102">"मोबाइल सेवा अॅक्टिव्हेट करा"</string> <string name="install_carrier_app_notification_text" msgid="2781317581274192728">"तुमचे नवीन सिम अॅक्टिव्हेट करण्यासाठी वाहकाचे अॅप डाउनलोड करा"</string> @@ -1336,7 +1336,7 @@ <string name="select_input_method" msgid="3971267998568587025">"इनपुट पद्धत निवडा"</string> <string name="show_ime" msgid="6406112007347443383">"भौतिक कीबोर्ड सक्रिय असताना त्यास स्क्रीनवर ठेवा"</string> <string name="hardware" msgid="1800597768237606953">"व्हर्च्युअल कीबोर्ड दर्शवा"</string> - <string name="select_keyboard_layout_notification_title" msgid="4427643867639774118">"वास्तविक कीबोर्ड कॉन्फिगर करा"</string> + <string name="select_keyboard_layout_notification_title" msgid="4427643867639774118">"वास्तविक कीबोर्ड कॉंफिगर करा"</string> <string name="select_keyboard_layout_notification_message" msgid="8835158247369158154">"भाषा आणि लेआउट निवडण्यासाठी टॅप करा"</string> <string name="fast_scroll_alphabet" msgid="8854435958703888376">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string> <string name="fast_scroll_numeric_alphabet" msgid="2529539945421557329">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string> @@ -1914,7 +1914,7 @@ <string name="demo_starting_message" msgid="6577581216125805905">"डेमो प्रारंभ करत आहे..."</string> <string name="demo_restarting_message" msgid="1160053183701746766">"डिव्हाइस रीसेट करत आहे..."</string> <string name="suspended_widget_accessibility" msgid="6331451091851326101">"<xliff:g id="LABEL">%1$s</xliff:g> अक्षम केले"</string> - <string name="conference_call" msgid="5731633152336490471">"कॉन्फरन्स कॉल"</string> + <string name="conference_call" msgid="5731633152336490471">"कॉंफरन्स कॉल"</string> <string name="tooltip_popup_title" msgid="7863719020269945722">"टूलटिप"</string> <string name="app_category_game" msgid="4534216074910244790">"गेम"</string> <string name="app_category_audio" msgid="8296029904794676222">"संगीत आणि ऑडिओ"</string> diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml index 2cdf709576f9..0326c23722c6 100644 --- a/core/res/res/values-ne/strings.xml +++ b/core/res/res/values-ne/strings.xml @@ -78,18 +78,18 @@ <string name="serviceNotProvisioned" msgid="8289333510236766193">"सेवाको व्यवस्था छैन।"</string> <string name="CLIRPermanent" msgid="166443681876381118">"तपाईं कलर ID सेटिङ परिवर्तन गर्न सक्नुहुन्न।"</string> <string name="RestrictedOnDataTitle" msgid="1500576417268169774">"कुनै पनि मोबाइल डेटा सेवा उपलब्ध छैन"</string> - <string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"आपतकालीन कल सेवा उपलब्ध छैन"</string> + <string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"आपत्कालीन कल सेवा उपलब्ध छैन"</string> <string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"कुनै पनि भ्वाइस सेवा उपलब्ध छैन"</string> <string name="RestrictedOnAllVoiceTitle" msgid="3982069078579103087">"कुनै पनि भ्वाइस वा आपातकालीन कल सेवा उपलब्ध छैन"</string> <string name="RestrictedStateContent" msgid="7693575344608618926">"तपाईंको सेवा प्रदायकले अस्थायी रूपमा निष्क्रिय पार्नुभएको"</string> <string name="RestrictedStateContentMsimTemplate" msgid="5228235722511044687">"SIM <xliff:g id="SIMNUMBER">%d</xliff:g> का लागि तपाईंको सेवा प्रदायकले अस्थायी रूपमा निष्क्रिय पार्नुभएको"</string> <string name="NetworkPreferenceSwitchTitle" msgid="1008329951315753038">"मोबाइल नेटवर्कमाथि पहुँच राख्न सकिएन"</string> <string name="NetworkPreferenceSwitchSummary" msgid="2086506181486324860">"रुचाइएको नेटवर्क परिवर्तन गरी हेर्नुहोस्। परिवर्तन गर्न ट्याप गर्नुहोस्।"</string> - <string name="EmergencyCallWarningTitle" msgid="1615688002899152860">"आपतकालीन कल सेवा अनुपलब्ध छ"</string> - <string name="EmergencyCallWarningSummary" msgid="1194185880092805497">"Wi‑Fi मार्फत आपतकालीन कल गर्न सकिँदैन"</string> + <string name="EmergencyCallWarningTitle" msgid="1615688002899152860">"आपत्कालीन कल सेवा अनुपलब्ध छ"</string> + <string name="EmergencyCallWarningSummary" msgid="1194185880092805497">"Wi‑Fi मार्फत आपत्कालीन कल गर्न सकिँदैन"</string> <string name="notification_channel_network_alert" msgid="4788053066033851841">"अलर्टहरू"</string> <string name="notification_channel_call_forward" msgid="8230490317314272406">"कल फर्वार्ड गर्ने सेवा"</string> - <string name="notification_channel_emergency_callback" msgid="54074839059123159">"आपतकालीन कलब्याक मोड"</string> + <string name="notification_channel_emergency_callback" msgid="54074839059123159">"आपत्कालीन कलब्याक मोड"</string> <string name="notification_channel_mobile_data_status" msgid="1941911162076442474">"मोबाइल डेटाको स्थिति"</string> <string name="notification_channel_sms" msgid="1243384981025535724">"SMS सन्देशहरू"</string> <string name="notification_channel_voice_mail" msgid="8457433203106654172">"भ्वाइस मेल सन्देशहरू"</string> @@ -241,7 +241,7 @@ <string name="global_action_power_off" msgid="4404936470711393203">"बन्द गर्नुहोस्"</string> <string name="global_action_power_options" msgid="1185286119330160073">"पावर"</string> <string name="global_action_restart" msgid="4678451019561687074">"पुनः सुरु गर्नुहोस्"</string> - <string name="global_action_emergency" msgid="1387617624177105088">"आपतकालीन"</string> + <string name="global_action_emergency" msgid="1387617624177105088">"आपत्कालीन"</string> <string name="global_action_bug_report" msgid="5127867163044170003">"बग रिपोर्ट"</string> <string name="global_action_logout" msgid="6093581310002476511">"सत्रको अन्त्य गर्नुहोस्"</string> <string name="global_action_screenshot" msgid="2610053466156478564">"स्क्रिनसट"</string> @@ -345,24 +345,24 @@ <string name="permdesc_processOutgoingCalls" msgid="7833149750590606334">"एपलाई अन्य नम्बरमा कल पुर्ननिर्देश वा समग्र कल परित्याग विकल्प सहित बहिर्गमन कल समयमा डायल गर्दाको नम्बर हेर्न अनुमति दिन्छ।"</string> <string name="permlab_answerPhoneCalls" msgid="4131324833663725855">"फोन कलहरूको जवाफ दिनुहोस्"</string> <string name="permdesc_answerPhoneCalls" msgid="894386681983116838">"एपलाई आगमन फोन कलको जवाफ दिन अनुमति दिन्छ।"</string> - <string name="permlab_receiveSms" msgid="505961632050451881">"पाठ सन्देशहरू (SMS) प्राप्त गर्नुहोस्"</string> + <string name="permlab_receiveSms" msgid="505961632050451881">"टेक्स्ट म्यासेजहरू (SMS) प्राप्त गर्नुहोस्"</string> <string name="permdesc_receiveSms" msgid="1797345626687832285">"एपलाई SMS सन्देशहरू प्राप्त गर्न र प्रक्रिया गर्न अनुमति दिन्छ। यसको मतलब अनुप्रयोगले तपाईंको उपकरणमा पठाइएको सन्देशहरू तपाईंलाई नदेखाईनै मोनिटर गर्न वा मेटाउन सक्दछ।"</string> <string name="permlab_receiveMms" msgid="4000650116674380275">"पाठ सन्देश (MMS) प्राप्त गर्नुहोस्"</string> <string name="permdesc_receiveMms" msgid="958102423732219710">"एपलाई MMS सन्देशहरू प्राप्त गर्न र प्रकृया गर्न अनुमति दिन्छ। यसको मतलब अनुप्रयोगले तपाईंको उपकरणमा पठाइएको सन्देशहरू तपाईंलाई नदेखाईनै मोनिटर गर्न वा मेटाउन सक्दछ।"</string> <string name="permlab_bindCellBroadcastService" msgid="586746677002040651">"मोबाइल प्रसारणसम्बन्धी सन्देशहरू फर्वार्ड गर्नुहोस्"</string> - <string name="permdesc_bindCellBroadcastService" msgid="6540910200973641606">"मोबाइल प्रसारणसम्बन्धी सन्देशहरू प्राप्त हुनासाथै तिनीहरूलाई फर्वार्ड गर्नका लागि यसले एपलाई मोबाइल प्रसारण मोड्युलमा जोडिने अनुमति दिन्छ। तपाईंलाई कतिपय स्थानमा आपत्कालीन अवस्थाका बारेमा जानकारी दिनका लागि मोबाइल प्रसारणसम्बन्धी अलर्टहरू पठाइन्छ। हानिकारक एपहरूले आपत्कालीन मोबाइल प्रसारण प्राप्त हुँदा तपाईंको यन्त्रलाई कार्य सम्पादन गर्ने वा सञ्चालित हुने क्रममा हस्तक्षेप गर्न सक्छन्।"</string> + <string name="permdesc_bindCellBroadcastService" msgid="6540910200973641606">"मोबाइल प्रसारणसम्बन्धी सन्देशहरू प्राप्त हुनासाथै तिनीहरूलाई फर्वार्ड गर्नका लागि यसले एपलाई मोबाइल प्रसारण मोड्युलमा जोडिने अनुमति दिन्छ। तपाईंलाई कतिपय स्थानमा आपत्कालीन अवस्थाका बारेमा जानकारी दिनका लागि मोबाइल प्रसारणसम्बन्धी अलर्टहरू पठाइन्छ। हानिकारक एपहरूले आपत्कालीन मोबाइल प्रसारण प्राप्त हुँदा तपाईंको यन्त्रलाई कार्य सम्पादन गर्ने वा सञ्चालित हुने क्रममा हस्तक्षेप गर्न सक्छन्।"</string> <string name="permlab_readCellBroadcasts" msgid="5869884450872137693">"सेल प्रसारित सन्देशहरू पढ्नुहोस्"</string> - <string name="permdesc_readCellBroadcasts" msgid="672513437331980168">"तपाईंको उपकरणद्वारा प्राप्त सेल प्रसारण सन्देशहरू एपलाई पढ्न अनुमति दिन्छ। सेल प्रसारण चेतावनीहरू केही स्थानहरूमा तपाईंलाई आपतकालीन गतिविधिहरूको बारेमा सचेत गराउन गरिएका छन्। खराब एपहरूले एउटा आपतकालीन सेल प्रसारण प्राप्त गर्दछ जब तपाईंको उपकरणको प्रदर्शन वा अपरेशनको साथ हस्तक्षेप गर्न सक्दछन्।"</string> + <string name="permdesc_readCellBroadcasts" msgid="672513437331980168">"तपाईंको उपकरणद्वारा प्राप्त सेल प्रसारण सन्देशहरू एपलाई पढ्न अनुमति दिन्छ। सेल प्रसारण चेतावनीहरू केही स्थानहरूमा तपाईंलाई आपत्कालीन गतिविधिहरूको बारेमा सचेत गराउन गरिएका छन्। खराब एपहरूले एउटा आपत्कालीन सेल प्रसारण प्राप्त गर्दछ जब तपाईंको उपकरणको प्रदर्शन वा अपरेशनको साथ हस्तक्षेप गर्न सक्दछन्।"</string> <string name="permlab_subscribedFeedsRead" msgid="217624769238425461">"सदस्य बनाइका फिडहरू पढ्नुहोस्"</string> <string name="permdesc_subscribedFeedsRead" msgid="6911349196661811865">"एपलाई अहिलेको समीकरण गरिएका सूचकहरू बारे विवरणहरू लिने अनुमति दिन्छ।"</string> <string name="permlab_sendSms" msgid="7757368721742014252">"SMS सन्देशहरू पठाउनुहोस् र हेर्नुहोस्"</string> <string name="permdesc_sendSms" msgid="6757089798435130769">"एपलाई SMS सन्देशहरू पठाउन अनुमति दिन्छ। यसले अप्रत्यासित चार्जहरूको परिणाम दिन सक्दछ। खराब एपहरूले तपाईंको पुष्टि बिना सन्देशहरू पठाएर तपाईंको पैसा खर्च गराउन सक्दछ।"</string> - <string name="permlab_readSms" msgid="5164176626258800297">"तपाईंका पाठ सन्देशहरू (SMS वा MMS) पढ्नुहोस्"</string> + <string name="permlab_readSms" msgid="5164176626258800297">"तपाईंका टेक्स्ट म्यासेजहरू (SMS वा MMS) पढ्नुहोस्"</string> <string name="permdesc_readSms" product="tablet" msgid="7912990447198112829">"यस अनुप्रयोगले तपाईंको ट्याब्लेटमा भण्डारण गरिएका सबै SMS (पाठ) सन्देशहरू पढ्न सक्छ।"</string> <string name="permdesc_readSms" product="tv" msgid="3054753345758011986">"यस अनुप्रयोगले तपाईंको Android टिभी यन्त्रमा भण्डार गरिएका सबै SMS.(पाठ) सन्देशहरू पढ्न सक्छ।"</string> <string name="permdesc_readSms" product="default" msgid="774753371111699782">"यस अनुप्रयोगले तपाईंको फोनमा भण्डारण गरिएका सबै SMS (पाठ) सन्देशहरू पढ्न सक्छ।"</string> - <string name="permlab_receiveWapPush" msgid="4223747702856929056">"पाठ सन्देशहरू (WAP) प्राप्त गर्नुहोस्"</string> - <string name="permdesc_receiveWapPush" msgid="1638677888301778457">"WAP सन्देशहरू प्राप्त गर्न र प्रशोधन गर्न एपलाई अनुमति दिन्छ। यो अनुमतिमा मोनिटर गर्ने वा तपाईँलाई पठाइएका सन्देशहरू तपाईँलाई नदेखाई मेट्ने क्षमता समावेश हुन्छ।"</string> + <string name="permlab_receiveWapPush" msgid="4223747702856929056">"टेक्स्ट म्यासेजहरू (WAP) प्राप्त गर्नुहोस्"</string> + <string name="permdesc_receiveWapPush" msgid="1638677888301778457">"WAP सन्देशहरू प्राप्त गर्न र प्रशोधन गर्न एपलाई अनुमति दिन्छ। यो अनुमतिमा मोनिटर गर्ने वा तपाईँलाई पठाइएका म्यासेजहरू तपाईँलाई नदेखाई मेट्ने क्षमता समावेश हुन्छ।"</string> <string name="permlab_getTasks" msgid="7460048811831750262">"चलिरहेका एपहरू पुनःबहाली गर्नुहोस्"</string> <string name="permdesc_getTasks" msgid="7388138607018233726">"वर्तमानमा र भरखरै चलिरहेका कार्यहरू बारेको सूचना पुनःबहाली गर्न एपलाई अनुमित दिन्छ। यसले उपकरणमा प्रयोग भएका अनुप्रयोगहरूको बारेमा सूचना पत्ता लगाउन एपलाई अनुमति दिन सक्छ।"</string> <string name="permlab_manageProfileAndDeviceOwners" msgid="639849495253987493">"प्रोफाइल र यन्त्र मालिकहरूको व्यवस्थापन गराउनुहोस्"</string> @@ -451,7 +451,7 @@ <string name="permdesc_vibrate" msgid="8733343234582083721">"एपलाई भाइब्रेटर नियन्त्रण गर्न अनुमति दिन्छ।"</string> <string name="permdesc_vibrator_state" msgid="7050024956594170724">"यो एपलाई कम्पनको स्थितिमाथि पहुँच राख्न दिनुहोस्।"</string> <string name="permlab_callPhone" msgid="1798582257194643320">"फोन नम्बरहरूमा सीधै कल गर्नुहोस्"</string> - <string name="permdesc_callPhone" msgid="5439809516131609109">"तपाईँको हस्तक्षेप बेगरै फोन नम्बर कल गर्न एपलाई अनुमति दिन्छ। यसले अनपेक्षित शुल्क वा कलहरू गराउन सक्छ। यसले एपलाई आपतकालीन नम्बरहरू कल गर्न अनुमति दिँदैन विचार गर्नुहोस्। खराब एपहरूले तपाईँको स्वीकार बिना कलहरू गरेर तपाईँलाई बढी पैसा तिराउन सक्छ।"</string> + <string name="permdesc_callPhone" msgid="5439809516131609109">"तपाईँको हस्तक्षेप बेगरै फोन नम्बर कल गर्न एपलाई अनुमति दिन्छ। यसले अनपेक्षित शुल्क वा कलहरू गराउन सक्छ। यसले एपलाई आपत्कालीन नम्बरहरू कल गर्न अनुमति दिँदैन विचार गर्नुहोस्। खराब एपहरूले तपाईँको स्वीकार बिना कलहरू गरेर तपाईँलाई बढी पैसा तिराउन सक्छ।"</string> <string name="permlab_accessImsCallService" msgid="442192920714863782">"IMS कल सेवा पहुँच गर्नुहोस्"</string> <string name="permdesc_accessImsCallService" msgid="6328551241649687162">"तपाईँको हस्तक्षेप बिना नै कल गर्न IMS सेवा प्रयोग गर्न एपलाई अनुमति दिन्छ।"</string> <string name="permlab_readPhoneState" msgid="8138526903259297969">"फोन स्थिति र पहिचान पढ्नुहोस्"</string> @@ -830,13 +830,13 @@ <string name="keyguard_password_enter_pin_password_code" msgid="7792964196473964340">"अनलक गर्न PIN कोड टाइप गर्नुहोस्"</string> <string name="keyguard_password_wrong_pin_code" msgid="8583732939138432793">"गलत PIN कोड।"</string> <string name="keyguard_label_text" msgid="3841953694564168384">"अनलक गर्न मेनु थिच्नुहोस् र त्यसपछि ० थिच्नुहोस्।"</string> - <string name="emergency_call_dialog_number_for_display" msgid="2978165477085612673">"आपतकालीन नम्बर"</string> + <string name="emergency_call_dialog_number_for_display" msgid="2978165477085612673">"आपत्कालीन नम्बर"</string> <string name="lockscreen_carrier_default" msgid="6192313772955399160">"कुनै सेवा छैन"</string> <string name="lockscreen_screen_locked" msgid="7364905540516041817">"स्क्रिन लक गरिएको।"</string> - <string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"अनलक वा आपतकालीन कल गर्न मेनु थिच्नुहोस्।"</string> + <string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"अनलक वा आपत्कालीन कल गर्न मेनु थिच्नुहोस्।"</string> <string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"अनलक गर्न मेनु थिच्नुहोस्।"</string> <string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"अनलक गर्नु ढाँचा खिच्नुहोस्"</string> - <string name="lockscreen_emergency_call" msgid="7549683825868928636">"आपतकालीन कल"</string> + <string name="lockscreen_emergency_call" msgid="7549683825868928636">"आपत्कालीन कल"</string> <string name="lockscreen_return_to_call" msgid="3156883574692006382">"कलमा फर्किनुहोस्"</string> <string name="lockscreen_pattern_correct" msgid="8050630103651508582">"सही!"</string> <string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"फेरि प्रयास गर्नुहोस्"</string> @@ -858,7 +858,7 @@ <string name="lockscreen_transport_stop_description" msgid="1449552232598355348">"रोक्नुहोस्"</string> <string name="lockscreen_transport_rew_description" msgid="7680106856221622779">"दोहोर्याउनुहोस्"</string> <string name="lockscreen_transport_ffw_description" msgid="4763794746640196772">"फास्ट फर्वार्ड"</string> - <string name="emergency_calls_only" msgid="3057351206678279851">"आपतकालीन कलहरू मात्र"</string> + <string name="emergency_calls_only" msgid="3057351206678279851">"आपत्कालीन कलहरू मात्र"</string> <string name="lockscreen_network_locked_message" msgid="2814046965899249635">"नेटवर्क लक छ"</string> <string name="lockscreen_sim_puk_locked_message" msgid="6618356415831082174">"SIM कार्ड PUK-लक गरिएको छ।"</string> <string name="lockscreen_sim_puk_locked_instructions" msgid="5307979043730860995">"प्रयोगकर्ता निर्देशक वा ग्राहक सेवा सम्पर्क हर्नुहोस्।"</string> @@ -1389,7 +1389,7 @@ <string name="ext_media_status_unmountable" msgid="7043574843541087748">"बिग्रेको"</string> <string name="ext_media_status_unsupported" msgid="5460509911660539317">"असमर्थित"</string> <string name="ext_media_status_ejecting" msgid="7532403368044013797">"निकाल्दै..."</string> - <string name="ext_media_status_formatting" msgid="774148701503179906">"फरम्याट गर्दै…"</string> + <string name="ext_media_status_formatting" msgid="774148701503179906">"फर्म्याट गर्दै…"</string> <string name="ext_media_status_missing" msgid="6520746443048867314">"सम्मिलित छैन"</string> <string name="activity_list_empty" msgid="4219430010716034252">"कुनै मिल्ने गतिविधि पाइएन।"</string> <string name="permlab_route_media_output" msgid="8048124531439513118">"मिडिया निकास दिशानिर्देश गराउनुहोस्"</string> @@ -1966,7 +1966,7 @@ <string name="etws_primary_default_message_earthquake" msgid="8401079517718280669">"शान्त रहनुहोस् र नजिकै आश्रयस्थल खोज्नुहोस्।"</string> <string name="etws_primary_default_message_tsunami" msgid="5828171463387976279">"तटीय क्षेत्र र नदीछेउका ठाउँहरू छाडी उच्च सतहमा अवस्थित कुनै अझ सुरक्षित ठाउँमा जानुहोस्।"</string> <string name="etws_primary_default_message_earthquake_and_tsunami" msgid="4888224011071875068">"शान्त रहनुहोस् र नजिकै आश्रयस्थल खोज्नुहोस्।"</string> - <string name="etws_primary_default_message_test" msgid="4583367373909549421">"आपतकालीन सन्देशहरूको परीक्षण"</string> + <string name="etws_primary_default_message_test" msgid="4583367373909549421">"आपत्कालीन सन्देशहरूको परीक्षण"</string> <string name="notification_reply_button_accessibility" msgid="5235776156579456126">"जवाफ दिनु…"</string> <string name="etws_primary_default_message_others" msgid="7958161706019130739"></string> <string name="mmcc_authentication_reject" msgid="4891965994643876369">"SIM मार्फत भ्वाइस कल गर्न मिल्दैन"</string> diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml index 94fad2521efa..698e9c47a7cd 100644 --- a/core/res/res/values-ur/strings.xml +++ b/core/res/res/values-ur/strings.xml @@ -274,8 +274,8 @@ <string name="notification_channel_security" msgid="8516754650348238057">"سیکیورٹی"</string> <string name="notification_channel_car_mode" msgid="2123919247040988436">"کار وضع"</string> <string name="notification_channel_account" msgid="6436294521740148173">"اکاؤنٹ اسٹیٹس"</string> - <string name="notification_channel_developer" msgid="1691059964407549150">"ڈیولپر کے پیغامات"</string> - <string name="notification_channel_developer_important" msgid="7197281908918789589">"اہم ڈیولپر پیغامات"</string> + <string name="notification_channel_developer" msgid="1691059964407549150">"ڈویلپر کے پیغامات"</string> + <string name="notification_channel_developer_important" msgid="7197281908918789589">"اہم ڈویلپر پیغامات"</string> <string name="notification_channel_updates" msgid="7907863984825495278">"اپ ڈیٹس"</string> <string name="notification_channel_network_status" msgid="2127687368725272809">"نیٹ ورک اسٹیٹس"</string> <string name="notification_channel_network_alerts" msgid="6312366315654526528">"نیٹ ورک الرٹس"</string> @@ -1218,7 +1218,7 @@ <string name="dump_heap_ready_notification" msgid="2302452262927390268">"<xliff:g id="PROC">%1$s</xliff:g> ہیپ ڈمپ تیار ہے"</string> <string name="dump_heap_notification_detail" msgid="8431586843001054050">"ہیپ ڈمپ جمع ہو گیا ہے۔ اشتراک کرنے کیلئے تھپتھپائیں۔"</string> <string name="dump_heap_title" msgid="4367128917229233901">"ہیپ ڈمپ کا اشتراک کریں؟"</string> - <string name="dump_heap_text" msgid="1692649033835719336">"<xliff:g id="PROC">%1$s</xliff:g> کارروائی اپنی میموری کی حد <xliff:g id="SIZE">%2$s</xliff:g> سے تجاوز کر گئی ہے۔ آپ کے لیے ایک ہیپ ڈمپ اس کے ڈیولپر کے ساتھ اشتراک کرنے کے لیے دستیاب ہے۔ محتاط رہیں: اس ہیپ ڈمپ میں آپ کی کوئی ایسی ذاتی معلومات بھی شامل ہو سکتی ہے جس تک ایپلیکیشن کو رسائی حاصل ہے۔"</string> + <string name="dump_heap_text" msgid="1692649033835719336">"<xliff:g id="PROC">%1$s</xliff:g> کارروائی اپنی میموری کی حد <xliff:g id="SIZE">%2$s</xliff:g> سے تجاوز کر گئی ہے۔ آپ کے لیے ایک ہیپ ڈمپ اس کے ڈویلپر کے ساتھ اشتراک کرنے کے لیے دستیاب ہے۔ محتاط رہیں: اس ہیپ ڈمپ میں آپ کی کوئی ایسی ذاتی معلومات بھی شامل ہو سکتی ہے جس تک ایپلیکیشن کو رسائی حاصل ہے۔"</string> <string name="dump_heap_system_text" msgid="6805155514925350849">"<xliff:g id="PROC">%1$s</xliff:g> پروسیس نے اپنی میموری کی حد <xliff:g id="SIZE">%2$s</xliff:g> سے بڑھا لی ہے۔ آپ کے اشتراک کرنے کے لیے ہیپ ڈمپ دستیاب ہے۔ محتاط رہیں: اس ہیپ ڈمپ میں حساس ذاتی معلومات ہو سکتی ہے، جس میں آپ کے ذریعے ٹائپ کردہ چیزیں شامل ہو سکتی ہیں، جس تک پروسیس کو رسائی حاصل ہو سکتی ہے۔"</string> <string name="dump_heap_ready_text" msgid="5849618132123045516">"<xliff:g id="PROC">%1$s</xliff:g> کے پروسیس کا ہیپ ڈمپ آپ کے اشتراک کے لیے دستیاب ہے۔ محتاط رہیں: اس ہیپ ڈمپ میں ممکنہ طور پر حساس ذاتی معلومات ہو سکتی ہے، جس میں آپ کے ذریعے ٹائپ کردہ چیزیں شامل ہو سکتی ہیں، جس تک پروسیس کو رسائی حاصل ہو سکتی ہے۔"</string> <string name="sendText" msgid="493003724401350724">"متن کیلئے ایک کارروائی منتخب کریں"</string> @@ -1896,7 +1896,7 @@ <string name="work_mode_turn_on" msgid="3662561662475962285">"آن کریں"</string> <string name="app_blocked_title" msgid="7353262160455028160">"ایپ دستیاب نہیں ہے"</string> <string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> ابھی دستیاب نہیں ہے۔"</string> - <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"یہ ایپ Android کے پرانے ورژن کے لئے بنائی گئی ہے اور ہو سکتا ہے صحیح طور پر کام نہ کرے۔ اپ ڈیٹس چیک کر کے آزمائیں یا ڈیولپر سے رابطہ کریں۔"</string> + <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"یہ ایپ Android کے پرانے ورژن کے لئے بنائی گئی ہے اور ہو سکتا ہے صحیح طور پر کام نہ کرے۔ اپ ڈیٹس چیک کر کے آزمائیں یا ڈویلپر سے رابطہ کریں۔"</string> <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"اپ ڈیٹ چیک کریں"</string> <string name="new_sms_notification_title" msgid="6528758221319927107">"آپ کے پاس نئے پیغامات ہیں"</string> <string name="new_sms_notification_content" msgid="3197949934153460639">"دیکھنے کیلئے SMS ایپ کھولیں"</string> diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index 64de32c9ad09..b74f96de33b1 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -3748,6 +3748,7 @@ <flag name="flagServiceHandlesDoubleTap" value="0x00000800" /> <!-- Has flag {@link android.accessibilityservice.AccessibilityServiceInfo#FLAG_REQUEST_MULTI_FINGER_GESTURES}. --> <flag name="flagRequestMultiFingerGestures" value="0x00001000" /> + <flag name="flagSendMotionEvents" value="0x0004000" /> </attr> <!-- Component name of an activity that allows the user to modify the settings for this service. This setting cannot be changed at runtime. --> diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml index 25c64a9f8781..e3ddbd8d25a2 100644 --- a/core/res/res/values/attrs_manifest.xml +++ b/core/res/res/values/attrs_manifest.xml @@ -3200,6 +3200,15 @@ then the system will set the same minimal height on all other activities in the task. It will also ignore any other minimal height attributes of non-root activities. --> <attr name="minHeight" /> + + <!-- Window layout affinity of this activity. Activities with the same window layout + affinity will share the same layout record. If an activity is launched in freeform window, + the activity will be launched to the latest position and size where any task, if the root + activity of that task shares the same window layout affinity with the activity being + launched. Window layout affinity is shared only among activities with the same UID. + + <p>By default activity doesn't share any affinity with other activities. --> + <attr name="windowLayoutAffinity" format="string" /> </declare-styleable> <!-- <code>restrict-update</code> tag restricts system apps from being updated unless the diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index e434ac97bfba..b4b634ab7b59 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -1686,6 +1686,21 @@ --> </string-array> + <!-- Optional IPsec algorithms enabled by this device, defaulting to empty. OEMs can override + it by providing a list of algorithm names in an overlay config.xml file. + + As Android releases new versions, more algorithms are becoming mandatory. Mandatory + algorithms will be automatically enabled on the device. Optional algorithms need + to be explicitly declared in this resource to be enabled. + * SDK level 28 makes the following algorithms mandatory : "cbc(aes)", "hmac(md5)", + "hmac(sha1)", "hmac(sha256)", "hmac(sha384)", "hmac(sha512)", "rfc4106(gcm(aes))" + * SDK level 30 makes the following algorithms mandatory : "rfc3686(ctr(aes))", + "xcbc(aes)", "rfc7539esp(chacha20,poly1305)" + --> + <string-array name="config_optionalIpSecAlgorithms" translatable="false"> + <!-- Add algorithm here --> + </string-array> + <!-- Boolean indicating if current platform supports bluetooth SCO for off call use cases --> <bool name="config_bluetooth_sco_off_call">true</bool> @@ -1803,6 +1818,9 @@ Note: This config is deprecated, please use config_defaultSms instead. --> <string name="default_sms_application" translatable="false">com.android.messaging</string> + <!-- Flag indicating whether the current device supports "Ask every time" for sms--> + <bool name="config_sms_ask_every_time_support">true</bool> + <!-- Flag indicating whether the current device allows data. If true, this means that the device supports data connectivity through the telephony network. @@ -4440,4 +4458,13 @@ <!-- WindowsManager JetPack display features --> <string name="config_display_features" translatable="false" /> + + <!-- Physical Display IDs of the display-devices that are swapped when a folding device folds. + This list is expected to contain two elements: the first is the display to use + when the device is folded, the second is the display to use when unfolded. If the array + is empty or the display IDs are not recognized, this feature is turned off and the value + ignored. + TODO: b/170470621 - remove once we can have multiple Internal displays in DMS as + well as a notification from DisplayStateManager. --> + <string-array name="config_internalFoldedPhysicalDisplayIds" translatable="false" /> </resources> diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index fe17eca35545..45bdff99fb9f 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -3046,6 +3046,7 @@ <public-group type="attr" first-id="0x01010617"> <public name="rollbackDataPolicy" /> <public name="allowClickWhenDisabled" /> + <public name="windowLayoutAffinity" /> </public-group> <public-group type="drawable" first-id="0x010800b5"> diff --git a/core/res/res/values/required_apps_managed_device.xml b/core/res/res/values/required_apps_managed_device.xml index 40db9dfbcd2c..e17d2147c8f0 100644 --- a/core/res/res/values/required_apps_managed_device.xml +++ b/core/res/res/values/required_apps_managed_device.xml @@ -21,6 +21,7 @@ Takes precedence over the disallowed apps lists. --> <string-array translatable="false" name="required_apps_managed_device"> <item>com.android.settings</item> + <item>com.android.systemui</item> <item>com.android.contacts</item> <item>com.android.dialer</item> <item>com.android.stk</item> <!-- Required by com.android.phone by certain carriers --> diff --git a/core/res/res/values/required_apps_managed_profile.xml b/core/res/res/values/required_apps_managed_profile.xml index c6b37e810f1a..6ed385ad50df 100644 --- a/core/res/res/values/required_apps_managed_profile.xml +++ b/core/res/res/values/required_apps_managed_profile.xml @@ -22,6 +22,7 @@ <string-array translatable="false" name="required_apps_managed_profile"> <item>com.android.contacts</item> <item>com.android.settings</item> + <item>com.android.systemui</item> <item>com.android.providers.downloads</item> <item>com.android.providers.downloads.ui</item> <item>com.android.documentsui</item> diff --git a/core/res/res/values/required_apps_managed_user.xml b/core/res/res/values/required_apps_managed_user.xml index 8800e5353776..a6fc573a1ebc 100644 --- a/core/res/res/values/required_apps_managed_user.xml +++ b/core/res/res/values/required_apps_managed_user.xml @@ -21,6 +21,7 @@ Takes precedence over the disallowed apps lists. --> <string-array translatable="false" name="required_apps_managed_user"> <item>com.android.settings</item> + <item>com.android.systemui</item> <item>com.android.contacts</item> <item>com.android.dialer</item> <item>com.android.stk</item> <!-- Required by com.android.phone by certain carriers --> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index fc489b1bbe9b..c2120de74d01 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -4494,6 +4494,10 @@ shown in the warning dialog about the accessibility shortcut. --> <string name="color_correction_feature_name">Color Correction</string> + <!-- TODO(b/170970602): remove translatable=false when RBC has official name and strings --> + <!-- Title of Reduce Bright Colors feature, shown in the warning dialog about the accessibility shortcut. [CHAR LIMIT=none] --> + <string name="reduce_bright_colors_feature_name" translatable="false">Reduce Bright Colors</string> + <!-- Text in toast to alert the user that the accessibility shortcut turned on an accessibility service. [CHAR LIMIT=none] --> <string name="accessibility_shortcut_enabling_service">Held volume keys. <xliff:g id="service_name" example="TalkBack">%1$s</xliff:g> turned on.</string> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 1249b17501c8..caa3dffb60dd 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -200,9 +200,6 @@ <java-symbol type="id" name="action2" /> <java-symbol type="id" name="action3" /> <java-symbol type="id" name="action4" /> - <java-symbol type="id" name="media_seamless" /> - <java-symbol type="id" name="media_seamless_image" /> - <java-symbol type="id" name="media_seamless_text" /> <java-symbol type="id" name="notification_media_seekbar_container" /> <java-symbol type="id" name="notification_media_content" /> <java-symbol type="id" name="notification_media_progress" /> @@ -313,6 +310,7 @@ <java-symbol type="bool" name="config_networkSamplingWakesDevice" /> <java-symbol type="bool" name="config_showMenuShortcutsWhenKeyboardPresent" /> <java-symbol type="bool" name="config_sip_wifi_only" /> + <java-symbol type="bool" name="config_sms_ask_every_time_support" /> <java-symbol type="bool" name="config_sms_capable" /> <java-symbol type="bool" name="config_sms_utf8_support" /> <java-symbol type="bool" name="config_mobile_data_capable" /> @@ -3195,6 +3193,9 @@ <!-- Network Recommendation --> <java-symbol type="string" name="config_defaultNetworkRecommendationProviderPackage" /> + <!-- Optional IPsec algorithms --> + <java-symbol type="array" name="config_optionalIpSecAlgorithms" /> + <!-- Whether allow 3rd party apps on internal storage. --> <java-symbol type="bool" name="config_allow3rdPartyAppOnInternal" /> @@ -3249,6 +3250,7 @@ <java-symbol type="string" name="accessibility_shortcut_disabling_service" /> <java-symbol type="string" name="color_inversion_feature_name" /> <java-symbol type="string" name="color_correction_feature_name" /> + <java-symbol type="string" name="reduce_bright_colors_feature_name" /> <java-symbol type="string" name="config_defaultAccessibilityService" /> <java-symbol type="string" name="accessibility_shortcut_spoken_feedback" /> @@ -4074,4 +4076,5 @@ <java-symbol type="array" name="config_keep_warming_services" /> <java-symbol type="string" name="config_display_features" /> + <java-symbol type="array" name="config_internalFoldedPhysicalDisplayIds" /> </resources> diff --git a/core/sysprop/Android.bp b/core/sysprop/Android.bp index 7f20a0ba6642..237ede2006ea 100644 --- a/core/sysprop/Android.bp +++ b/core/sysprop/Android.bp @@ -19,3 +19,11 @@ sysprop_library { api_packages: ["android.sysprop"], vendor_available: false, } + +sysprop_library { + name: "com.android.sysprop.watchdog", + srcs: ["WatchdogProperties.sysprop"], + property_owner: "Platform", + api_packages: ["android.sysprop"], + vendor_available: false, +} diff --git a/core/sysprop/WatchdogProperties.sysprop b/core/sysprop/WatchdogProperties.sysprop new file mode 100644 index 000000000000..1bcc773a9a5d --- /dev/null +++ b/core/sysprop/WatchdogProperties.sysprop @@ -0,0 +1,45 @@ +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +module: "android.sysprop.WatchdogProperties" +owner: Platform + +# To escape the watchdog timeout loop, fatal reboot the system when +# watchdog timed out 'fatal_count' times in 'fatal_window_second' +# seconds, if both values are not 0. Default value of both is 0. +prop { + api_name: "fatal_count" + type: Integer + prop_name: "framework_watchdog.fatal_count" + scope: Internal + access: Readonly +} + +prop { + api_name: "fatal_window_second" + type: Integer + prop_name: "framework_watchdog.fatal_window.second" + scope: Internal + access: Readonly +} + +# The fatal counting can be disabled by setting property +# 'is_fatal_ignore' to true. +prop { + api_name: "is_fatal_ignore" + type: Boolean + prop_name: "persist.debug.framework_watchdog.fatal_ignore" + scope: Internal + access: Readonly +} diff --git a/core/sysprop/api/com.android.sysprop.localization-current.txt b/core/sysprop/api/com.android.sysprop.localization-current.txt index fe4f4578683c..e69de29bb2d1 100644 --- a/core/sysprop/api/com.android.sysprop.localization-current.txt +++ b/core/sysprop/api/com.android.sysprop.localization-current.txt @@ -1,9 +0,0 @@ -props { - module: "android.sysprop.LocalizationProperties" - prop { - api_name: "locale_filter" - type: String - scope: Internal - prop_name: "ro.localization.locale_filter" - } -} diff --git a/core/sysprop/api/com.android.sysprop.watchdog-current.txt b/core/sysprop/api/com.android.sysprop.watchdog-current.txt new file mode 100644 index 000000000000..e69de29bb2d1 --- /dev/null +++ b/core/sysprop/api/com.android.sysprop.watchdog-current.txt diff --git a/core/sysprop/api/com.android.sysprop.watchdog-latest.txt b/core/sysprop/api/com.android.sysprop.watchdog-latest.txt new file mode 100644 index 000000000000..d901aef945c9 --- /dev/null +++ b/core/sysprop/api/com.android.sysprop.watchdog-latest.txt @@ -0,0 +1,20 @@ +props { + module: "android.sysprop.WatchdogProperties" + prop { + api_name: "fatal_count" + type: Integer + scope: Internal + prop_name: "framework_watchdog.fatal_count" + } + prop { + api_name: "fatal_window_second" + type: Integer + scope: Internal + prop_name: "framework_watchdog.fatal_window.second" + } + prop { + api_name: "is_fatal_ignore" + scope: Internal + prop_name: "persist.debug.framework_watchdog.fatal_ignore" + } +} diff --git a/core/tests/coretests/Android.bp b/core/tests/coretests/Android.bp index c6ef0948174a..e2b975ff78ac 100644 --- a/core/tests/coretests/Android.bp +++ b/core/tests/coretests/Android.bp @@ -54,7 +54,6 @@ android_test { "android.test.mock", "framework-atb-backward-compatibility", "framework", - "icing-java-proto-lite", "ext", "framework-res", ], diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml index 38dce150d85c..bb826deb4eff 100644 --- a/core/tests/coretests/AndroidManifest.xml +++ b/core/tests/coretests/AndroidManifest.xml @@ -190,6 +190,17 @@ </intent-filter> </activity> + <activity android:name="android.widget.CustomInputConnectionEditTextActivity" + android:label="CustomInputConnectionEditTextActivity" + android:screenOrientation="portrait" + android:exported="true" + android:theme="@android:style/Theme.Material.Light"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" /> + </intent-filter> + </activity> + <activity android:name="android.widget.TextViewActivity" android:label="TextViewActivity" android:screenOrientation="portrait" diff --git a/core/tests/coretests/res/layout/activity_custom_input_connection_edit_text.xml b/core/tests/coretests/res/layout/activity_custom_input_connection_edit_text.xml new file mode 100644 index 000000000000..c4db8becaf48 --- /dev/null +++ b/core/tests/coretests/res/layout/activity_custom_input_connection_edit_text.xml @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2020 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <android.widget.CustomInputConnectionEditText + android:id="@+id/edittext1" + android:layout_width="match_parent" + android:layout_height="wrap_content" /> + + <android.widget.CustomInputConnectionEditText + android:id="@+id/edittext2" + android:layout_width="match_parent" + android:layout_height="wrap_content" /> + +</LinearLayout> diff --git a/core/tests/coretests/src/android/app/appsearch/SearchResultsTest.java b/core/tests/coretests/src/android/app/appsearch/SearchResultsTest.java deleted file mode 100644 index ac0f44bb17e4..000000000000 --- a/core/tests/coretests/src/android/app/appsearch/SearchResultsTest.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.app.appsearch; - -import static com.google.common.truth.Truth.assertThat; - -import static org.testng.Assert.assertThrows; - -import android.app.appsearch.proto.DocumentProto; -import android.app.appsearch.proto.SearchResultProto; - -import androidx.test.filters.SmallTest; - -import org.junit.Test; - -@SmallTest -public class SearchResultsTest { - - @Test - public void testSearchResultsEqual() { - final String uri = "testUri"; - final String schemaType = "testSchema"; - SearchResultProto.ResultProto result1 = SearchResultProto.ResultProto.newBuilder() - .setDocument(DocumentProto.newBuilder() - .setUri(uri) - .setSchema(schemaType) - .build()) - .build(); - SearchResultProto searchResults1 = SearchResultProto.newBuilder() - .addResults(result1) - .build(); - SearchResults res1 = new SearchResults(searchResults1); - SearchResultProto.ResultProto result2 = SearchResultProto.ResultProto.newBuilder() - .setDocument(DocumentProto.newBuilder() - .setUri(uri) - .setSchema(schemaType) - .build()) - .build(); - SearchResultProto searchResults2 = SearchResultProto.newBuilder() - .addResults(result2) - .build(); - SearchResults res2 = new SearchResults(searchResults2); - assertThat(res1.toString()).isEqualTo(res2.toString()); - } - - @Test - public void buildSearchSpecWithoutTermMatchType() { - assertThrows(RuntimeException.class, () -> new SearchSpec.Builder() - .setSchemaTypes("testSchemaType") - .build()); - } -} diff --git a/core/tests/coretests/src/android/app/appsearch/AppSearchEmailTest.java b/core/tests/coretests/src/android/app/appsearch/external/app/AppSearchEmailTest.java index 6aa16cc1e323..ac2d4b5c5f7d 100644 --- a/core/tests/coretests/src/android/app/appsearch/AppSearchEmailTest.java +++ b/core/tests/coretests/src/android/app/appsearch/external/app/AppSearchEmailTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 The Android Open Source Project + * Copyright 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,11 +18,8 @@ package android.app.appsearch; import static com.google.common.truth.Truth.assertThat; -import androidx.test.filters.SmallTest; - import org.junit.Test; -@SmallTest public class AppSearchEmailTest { @Test diff --git a/core/tests/coretests/src/android/app/appsearch/AppSearchDocumentTest.java b/core/tests/coretests/src/android/app/appsearch/external/app/GenericDocumentTest.java index 54a281f2a931..1f2c12bca028 100644 --- a/core/tests/coretests/src/android/app/appsearch/AppSearchDocumentTest.java +++ b/core/tests/coretests/src/android/app/appsearch/external/app/GenericDocumentTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 The Android Open Source Project + * Copyright 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,36 +18,25 @@ package android.app.appsearch; import static com.google.common.truth.Truth.assertThat; -import static org.testng.Assert.assertThrows; - -import android.app.appsearch.proto.DocumentProto; -import android.app.appsearch.proto.PropertyProto; -import android.app.appsearch.protobuf.ByteString; - -import androidx.test.filters.SmallTest; - +import static org.testng.Assert.expectThrows; import org.junit.Test; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; - -@SmallTest -public class AppSearchDocumentTest { +public class GenericDocumentTest { private static final byte[] sByteArray1 = new byte[]{(byte) 1, (byte) 2, (byte) 3}; - private static final byte[] sByteArray2 = new byte[]{(byte) 4, (byte) 5, (byte) 6}; - private static final AppSearchDocument sDocumentProperties1 = new AppSearchDocument + private static final byte[] sByteArray2 = new byte[]{(byte) 4, (byte) 5, (byte) 6, (byte) 7}; + private static final GenericDocument sDocumentProperties1 = new GenericDocument .Builder("sDocumentProperties1", "sDocumentPropertiesSchemaType1") + .setCreationTimestampMillis(12345L) .build(); - private static final AppSearchDocument sDocumentProperties2 = new AppSearchDocument + private static final GenericDocument sDocumentProperties2 = new GenericDocument .Builder("sDocumentProperties2", "sDocumentPropertiesSchemaType2") + .setCreationTimestampMillis(6789L) .build(); @Test public void testDocumentEquals_Identical() { - AppSearchDocument document1 = new AppSearchDocument.Builder("uri1", "schemaType1") + GenericDocument document1 = new GenericDocument.Builder("uri1", "schemaType1") .setCreationTimestampMillis(5L) .setTtlMillis(1L) .setProperty("longKey1", 1L, 2L, 3L) @@ -57,7 +46,7 @@ public class AppSearchDocumentTest { .setProperty("byteKey1", sByteArray1, sByteArray2) .setProperty("documentKey1", sDocumentProperties1, sDocumentProperties2) .build(); - AppSearchDocument document2 = new AppSearchDocument.Builder("uri1", "schemaType1") + GenericDocument document2 = new GenericDocument.Builder("uri1", "schemaType1") .setCreationTimestampMillis(5L) .setTtlMillis(1L) .setProperty("longKey1", 1L, 2L, 3L) @@ -73,7 +62,7 @@ public class AppSearchDocumentTest { @Test public void testDocumentEquals_DifferentOrder() { - AppSearchDocument document1 = new AppSearchDocument.Builder("uri1", "schemaType1") + GenericDocument document1 = new GenericDocument.Builder("uri1", "schemaType1") .setCreationTimestampMillis(5L) .setProperty("longKey1", 1L, 2L, 3L) .setProperty("byteKey1", sByteArray1, sByteArray2) @@ -84,7 +73,7 @@ public class AppSearchDocumentTest { .build(); // Create second document with same parameter but different order. - AppSearchDocument document2 = new AppSearchDocument.Builder("uri1", "schemaType1") + GenericDocument document2 = new GenericDocument.Builder("uri1", "schemaType1") .setCreationTimestampMillis(5L) .setProperty("booleanKey1", true, false, true) .setProperty("documentKey1", sDocumentProperties1, sDocumentProperties2) @@ -99,13 +88,13 @@ public class AppSearchDocumentTest { @Test public void testDocumentEquals_Failure() { - AppSearchDocument document1 = new AppSearchDocument.Builder("uri1", "schemaType1") + GenericDocument document1 = new GenericDocument.Builder("uri1", "schemaType1") .setCreationTimestampMillis(5L) .setProperty("longKey1", 1L, 2L, 3L) .build(); // Create second document with same order but different value. - AppSearchDocument document2 = new AppSearchDocument.Builder("uri1", "schemaType1") + GenericDocument document2 = new GenericDocument.Builder("uri1", "schemaType1") .setCreationTimestampMillis(5L) .setProperty("longKey1", 1L, 2L, 4L) // Different .build(); @@ -115,13 +104,13 @@ public class AppSearchDocumentTest { @Test public void testDocumentEquals_Failure_RepeatedFieldOrder() { - AppSearchDocument document1 = new AppSearchDocument.Builder("uri1", "schemaType1") + GenericDocument document1 = new GenericDocument.Builder("uri1", "schemaType1") .setCreationTimestampMillis(5L) .setProperty("booleanKey1", true, false, true) .build(); // Create second document with same order but different value. - AppSearchDocument document2 = new AppSearchDocument.Builder("uri1", "schemaType1") + GenericDocument document2 = new GenericDocument.Builder("uri1", "schemaType1") .setCreationTimestampMillis(5L) .setProperty("booleanKey1", true, true, false) // Different .build(); @@ -131,11 +120,10 @@ public class AppSearchDocumentTest { @Test public void testDocumentGetSingleValue() { - AppSearchDocument document = new AppSearchDocument.Builder("uri1", "schemaType1") + GenericDocument document = new GenericDocument.Builder("uri1", "schemaType1") .setCreationTimestampMillis(5L) .setScore(1) .setTtlMillis(1L) - .setScore(1) .setProperty("longKey1", 1L) .setProperty("doubleKey1", 1.0) .setProperty("booleanKey1", true) @@ -159,7 +147,7 @@ public class AppSearchDocumentTest { @Test public void testDocumentGetArrayValues() { - AppSearchDocument document = new AppSearchDocument.Builder("uri1", "schemaType1") + GenericDocument document = new GenericDocument.Builder("uri1", "schemaType1") .setCreationTimestampMillis(5L) .setProperty("longKey1", 1L, 2L, 3L) .setProperty("doubleKey1", 1.0, 2.0, 3.0) @@ -185,8 +173,51 @@ public class AppSearchDocumentTest { } @Test + public void testDocument_ToString() throws Exception { + GenericDocument document = new GenericDocument.Builder("uri1", "schemaType1") + .setCreationTimestampMillis(5L) + .setProperty("longKey1", 1L, 2L, 3L) + .setProperty("doubleKey1", 1.0, 2.0, 3.0) + .setProperty("booleanKey1", true, false, true) + .setProperty("stringKey1", "String1", "String2", "String3") + .setProperty("byteKey1", sByteArray1, sByteArray2) + .setProperty("documentKey1", sDocumentProperties1, sDocumentProperties2) + .build(); + String exceptedString = "{ key: 'creationTimestampMillis' value: 5 } " + + "{ key: 'namespace' value: } " + + "{ key: 'properties' value: " + + "{ key: 'booleanKey1' value: [ 'true' 'false' 'true' ] } " + + "{ key: 'byteKey1' value: " + + "{ key: 'byteArray' value: [ '1' '2' '3' ] } " + + "{ key: 'byteArray' value: [ '4' '5' '6' '7' ] } } " + + "{ key: 'documentKey1' value: [ '" + + "{ key: 'creationTimestampMillis' value: 12345 } " + + "{ key: 'namespace' value: } " + + "{ key: 'properties' value: } " + + "{ key: 'schemaType' value: sDocumentPropertiesSchemaType1 } " + + "{ key: 'score' value: 0 } " + + "{ key: 'ttlMillis' value: 0 } " + + "{ key: 'uri' value: sDocumentProperties1 } ' '" + + "{ key: 'creationTimestampMillis' value: 6789 } " + + "{ key: 'namespace' value: } " + + "{ key: 'properties' value: } " + + "{ key: 'schemaType' value: sDocumentPropertiesSchemaType2 } " + + "{ key: 'score' value: 0 } " + + "{ key: 'ttlMillis' value: 0 } " + + "{ key: 'uri' value: sDocumentProperties2 } ' ] } " + + "{ key: 'doubleKey1' value: [ '1.0' '2.0' '3.0' ] } " + + "{ key: 'longKey1' value: [ '1' '2' '3' ] } " + + "{ key: 'stringKey1' value: [ 'String1' 'String2' 'String3' ] } } " + + "{ key: 'schemaType' value: schemaType1 } " + + "{ key: 'score' value: 0 } " + + "{ key: 'ttlMillis' value: 0 } " + + "{ key: 'uri' value: uri1 } "; + assertThat(document.toString()).isEqualTo(exceptedString); + } + + @Test public void testDocumentGetValues_DifferentTypes() { - AppSearchDocument document = new AppSearchDocument.Builder("uri1", "schemaType1") + GenericDocument document = new GenericDocument.Builder("uri1", "schemaType1") .setScore(1) .setProperty("longKey1", 1L) .setProperty("booleanKey1", true, false, true) @@ -213,53 +244,8 @@ public class AppSearchDocumentTest { @Test public void testDocumentInvalid() { - AppSearchDocument.Builder builder = new AppSearchDocument.Builder("uri1", "schemaType1"); - assertThrows( + GenericDocument.Builder builder = new GenericDocument.Builder("uri1", "schemaType1"); + expectThrows( IllegalArgumentException.class, () -> builder.setProperty("test", new boolean[]{})); } - - @Test - public void testDocumentProtoPopulation() { - AppSearchDocument document = new AppSearchDocument.Builder("uri1", "schemaType1") - .setCreationTimestampMillis(5L) - .setScore(1) - .setTtlMillis(1L) - .setProperty("longKey1", 1L) - .setProperty("doubleKey1", 1.0) - .setProperty("booleanKey1", true) - .setProperty("stringKey1", "test-value1") - .setProperty("byteKey1", sByteArray1) - .setProperty("documentKey1", sDocumentProperties1) - .build(); - - // Create the Document proto. Need to sort the property order by key. - DocumentProto.Builder documentProtoBuilder = DocumentProto.newBuilder() - .setUri("uri1") - .setSchema("schemaType1") - .setCreationTimestampMs(5L) - .setScore(1) - .setTtlMs(1L) - .setNamespace(""); - HashMap<String, PropertyProto.Builder> propertyProtoMap = new HashMap<>(); - propertyProtoMap.put("longKey1", - PropertyProto.newBuilder().setName("longKey1").addInt64Values(1L)); - propertyProtoMap.put("doubleKey1", - PropertyProto.newBuilder().setName("doubleKey1").addDoubleValues(1.0)); - propertyProtoMap.put("booleanKey1", - PropertyProto.newBuilder().setName("booleanKey1").addBooleanValues(true)); - propertyProtoMap.put("stringKey1", - PropertyProto.newBuilder().setName("stringKey1").addStringValues("test-value1")); - propertyProtoMap.put("byteKey1", - PropertyProto.newBuilder().setName("byteKey1").addBytesValues( - ByteString.copyFrom(sByteArray1))); - propertyProtoMap.put("documentKey1", - PropertyProto.newBuilder().setName("documentKey1") - .addDocumentValues(sDocumentProperties1.getProto())); - List<String> sortedKey = new ArrayList<>(propertyProtoMap.keySet()); - Collections.sort(sortedKey); - for (int i = 0; i < sortedKey.size(); i++) { - documentProtoBuilder.addProperties(propertyProtoMap.get(sortedKey.get(i))); - } - assertThat(document.getProto()).isEqualTo(documentProtoBuilder.build()); - } } diff --git a/core/tests/coretests/src/android/app/appsearch/external/app/SearchResultsTest.java b/core/tests/coretests/src/android/app/appsearch/external/app/SearchResultsTest.java new file mode 100644 index 000000000000..acbf11ae8669 --- /dev/null +++ b/core/tests/coretests/src/android/app/appsearch/external/app/SearchResultsTest.java @@ -0,0 +1,30 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app.appsearch; + +import static org.testng.Assert.expectThrows; + +import org.junit.Test; + +public class SearchResultsTest { + @Test + public void buildSearchSpecWithoutTermMatchType() { + expectThrows(RuntimeException.class, () -> new SearchSpec.Builder() + .setSchemaTypes("testSchemaType") + .build()); + } +} diff --git a/core/tests/coretests/src/android/app/appsearch/impl/CustomerDocumentTest.java b/core/tests/coretests/src/android/app/appsearch/external/app/customer/CustomerDocumentTest.java index b29483c2e3b3..2c7c35feface 100644 --- a/core/tests/coretests/src/android/app/appsearch/impl/CustomerDocumentTest.java +++ b/core/tests/coretests/src/android/app/appsearch/external/app/customer/CustomerDocumentTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 The Android Open Source Project + * Copyright 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,33 +14,30 @@ * limitations under the License. */ -package android.app.appsearch.impl; - -import static com.google.common.truth.Truth.assertThat; +package android.app.appsearch.customer; import android.annotation.NonNull; -import android.app.appsearch.AppSearchDocument; +import android.app.appsearch.GenericDocument; -import androidx.test.filters.SmallTest; +import static com.google.common.truth.Truth.assertThat; import org.junit.Test; /** - * Tests that {@link AppSearchDocument} and {@link AppSearchDocument.Builder} are extendable by + * Tests that {@link GenericDocument} and {@link GenericDocument.Builder} are extendable by * developers. * - * <p>This class is intentionally in a different package than {@link AppSearchDocument} to make sure + * <p>This class is intentionally in a different package than {@link GenericDocument} to make sure * there are no package-private methods required for external developers to add custom types. */ -@SmallTest public class CustomerDocumentTest { private static byte[] sByteArray1 = new byte[]{(byte) 1, (byte) 2, (byte) 3}; private static byte[] sByteArray2 = new byte[]{(byte) 4, (byte) 5, (byte) 6}; - private static AppSearchDocument sDocumentProperties1 = new AppSearchDocument + private static GenericDocument sDocumentProperties1 = new GenericDocument .Builder("sDocumentProperties1", "sDocumentPropertiesSchemaType1") .build(); - private static AppSearchDocument sDocumentProperties2 = new AppSearchDocument + private static GenericDocument sDocumentProperties2 = new GenericDocument .Builder("sDocumentProperties2", "sDocumentPropertiesSchemaType2") .build(); @@ -77,19 +74,21 @@ public class CustomerDocumentTest { /** * An example document type for test purposes, defined outside of - * {@link android.app.appsearch.AppSearch} (the way an external developer would define it). + * {@link GenericDocument} (the way an external developer would define + * it). */ - private static class CustomerDocument extends AppSearchDocument { - private CustomerDocument(AppSearchDocument document) { + private static class CustomerDocument extends GenericDocument { + private CustomerDocument(GenericDocument document) { super(document); } - public static class Builder extends AppSearchDocument.Builder<CustomerDocument.Builder> { + public static class Builder extends GenericDocument.Builder<CustomerDocument.Builder> { private Builder(@NonNull String uri) { super(uri, "customerDocument"); } @Override + @NonNull public CustomerDocument build() { return new CustomerDocument(super.build()); } diff --git a/core/tests/coretests/src/android/hardware/display/VirtualDisplayTest.java b/core/tests/coretests/src/android/hardware/display/VirtualDisplayTest.java index 0f6284d22d10..01cf311f63b1 100644 --- a/core/tests/coretests/src/android/hardware/display/VirtualDisplayTest.java +++ b/core/tests/coretests/src/android/hardware/display/VirtualDisplayTest.java @@ -362,14 +362,12 @@ public class VirtualDisplayTest extends AndroidTestCase { private final class TestPresentation extends Presentation { private final int mColor; - private final int mWindowType; private final int mWindowFlags; public TestPresentation(Context context, Display display, int color, int windowType, int windowFlags) { - super(context, display); + super(context, display, 0 /* theme */, windowType); mColor = color; - mWindowType = windowType; mWindowFlags = windowFlags; } @@ -378,7 +376,6 @@ public class VirtualDisplayTest extends AndroidTestCase { super.onCreate(savedInstanceState); setTitle(TAG); - getWindow().setType(mWindowType); getWindow().addFlags(mWindowFlags); // Create a solid color image to use as the content of the presentation. diff --git a/core/tests/coretests/src/android/text/TextShaperTest.java b/core/tests/coretests/src/android/text/TextShaperTest.java new file mode 100644 index 000000000000..8237cb0bb243 --- /dev/null +++ b/core/tests/coretests/src/android/text/TextShaperTest.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.text; + +import static com.google.common.truth.Truth.assertThat; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +@SmallTest +@RunWith(AndroidJUnit4.class) +public class TextShaperTest { + + @Test + public void testFontWithPath() { + TextPaint p = new TextPaint(); + p.setFontFeatureSettings("'wght' 900"); + TextShaper.shapeText("a", 0, 1, TextDirectionHeuristics.LTR, p, + (start, end, glyphs, paint) -> { + // This test only passes if the font of the Latin font is variable font. + assertThat(glyphs.getFont(0).getFile()).isNotNull(); + }); + } +} diff --git a/core/tests/coretests/src/android/text/TextUtilsTest.java b/core/tests/coretests/src/android/text/TextUtilsTest.java index 285ad9fae13c..4e49118f438c 100644 --- a/core/tests/coretests/src/android/text/TextUtilsTest.java +++ b/core/tests/coretests/src/android/text/TextUtilsTest.java @@ -16,6 +16,8 @@ package android.text; +import static android.text.TextUtils.formatSimple; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; @@ -794,4 +796,53 @@ public class TextUtilsTest { assertEquals("", TextUtils.trimToLengthWithEllipsis("", 3)); assertNull(TextUtils.trimToLengthWithEllipsis(null, 3)); } + + @Test + public void testFormatSimple_Types() { + assertEquals("true", formatSimple("%b", true)); + assertEquals("false", formatSimple("%b", false)); + assertEquals("true", formatSimple("%b", this)); + assertEquals("false", formatSimple("%b", new Object[] { null })); + + assertEquals("!", formatSimple("%c", '!')); + + assertEquals("42", formatSimple("%d", 42)); + assertEquals("281474976710656", formatSimple("%d", 281474976710656L)); + + assertEquals("3.14159", formatSimple("%f", 3.14159)); + assertEquals("NaN", formatSimple("%f", Float.NaN)); + + assertEquals("example", formatSimple("%s", "example")); + assertEquals("null", formatSimple("%s", new Object[] { null })); + + assertEquals("2a", formatSimple("%x", 42)); + assertEquals("1000000000000", formatSimple("%x", 281474976710656L)); + + assertEquals("%", formatSimple("%%")); + } + + @Test + public void testFormatSimple_Empty() { + assertEquals("", formatSimple("")); + } + + @Test + public void testFormatSimple_Typical() { + assertEquals("String foobar and %% number -42 together", + formatSimple("String %s%s and %%%% number %d%d together", "foo", "bar", -4, 2)); + } + + @Test + public void testFormatSimple_Mismatch() { + try { + formatSimple("%s"); + fail(); + } catch (IllegalArgumentException expected) { + } + try { + formatSimple("%s", "foo", "bar"); + fail(); + } catch (IllegalArgumentException expected) { + } + } } diff --git a/core/tests/coretests/src/android/text/format/DateFormatTest.java b/core/tests/coretests/src/android/text/format/DateFormatTest.java index a3434e885012..212cc44eefab 100644 --- a/core/tests/coretests/src/android/text/format/DateFormatTest.java +++ b/core/tests/coretests/src/android/text/format/DateFormatTest.java @@ -21,13 +21,19 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import android.compat.testing.PlatformCompatChangeRule; import android.icu.text.DateFormatSymbols; import android.platform.test.annotations.Presubmit; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; +import libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges; +import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges; + +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.TestRule; import org.junit.runner.RunWith; import java.util.Arrays; @@ -38,6 +44,9 @@ import java.util.Locale; @RunWith(AndroidJUnit4.class) public class DateFormatTest { + @Rule + public TestRule compatChangeRule = new PlatformCompatChangeRule(); + @Test public void testHasDesignator() { assertTrue(DateFormat.hasDesignator("hh:mm:ss", DateFormat.MINUTE)); @@ -135,4 +144,29 @@ public class DateFormatTest { private static String best(Locale l, String skeleton) { return DateFormat.getBestDateTimePattern(l, skeleton); } + + @Test + @EnableCompatChanges({DateFormat.DISALLOW_DUPLICATE_FIELD_IN_SKELETON}) + public void testGetBestDateTimePattern_disableDuplicateField() { + assertIllegalArgumentException(Locale.US, "jmma"); + assertIllegalArgumentException(Locale.US, "ahmma"); + } + + @Test + @DisableCompatChanges({DateFormat.DISALLOW_DUPLICATE_FIELD_IN_SKELETON}) + public void testGetBestDateTimePattern_enableDuplicateField() { + // en-US uses 12-hour format by default. + assertEquals("h:mm a", DateFormat.getBestDateTimePattern(Locale.US, "jmma")); + assertEquals("h:mm a", DateFormat.getBestDateTimePattern(Locale.US, "ahmma")); + } + + private static void assertIllegalArgumentException(Locale l, String skeleton) { + try { + DateFormat.getBestDateTimePattern(l, skeleton); + fail("getBestDateTimePattern() does not fail with Locale: " + l + + " skeleton: " + skeleton); + } catch (IllegalArgumentException expected) { + // ignored + } + } } diff --git a/core/tests/coretests/src/android/text/format/OWNERS b/core/tests/coretests/src/android/text/format/OWNERS new file mode 100644 index 000000000000..32adc12bb901 --- /dev/null +++ b/core/tests/coretests/src/android/text/format/OWNERS @@ -0,0 +1,3 @@ +# Inherits OWNERS from parent directory, plus the following + +vichang@google.com diff --git a/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java b/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java index 8412e53060c7..b98ce308ae3b 100644 --- a/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java +++ b/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java @@ -27,6 +27,7 @@ import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -89,6 +90,7 @@ public class InsetsAnimationControlImplTest { mInsetsState = new InsetsState(); mInsetsState.getSource(ITYPE_STATUS_BAR).setFrame(new Rect(0, 0, 500, 100)); mInsetsState.getSource(ITYPE_NAVIGATION_BAR).setFrame(new Rect(400, 0, 500, 500)); + doNothing().when(mMockController).onRequestedVisibilityChanged(any()); InsetsSourceConsumer topConsumer = new InsetsSourceConsumer(ITYPE_STATUS_BAR, mInsetsState, () -> mMockTransaction, mMockController); topConsumer.setControl( @@ -130,7 +132,7 @@ public class InsetsAnimationControlImplTest { public void testChangeInsets() { mController.setInsetsAndAlpha(Insets.of(0, 30, 40, 0), 1f /* alpha */, 0f /* fraction */); - mController.applyChangeInsets(new InsetsState()); + mController.applyChangeInsets(null /* outState */); assertEquals(Insets.of(0, 30, 40, 0), mController.getCurrentInsets()); assertEquals(1f, mController.getCurrentAlpha(), 1f - mController.getCurrentAlpha()); @@ -150,7 +152,7 @@ public class InsetsAnimationControlImplTest { public void testChangeAlphaNoInsets() { Insets initialInsets = mController.getCurrentInsets(); mController.setInsetsAndAlpha(null, 0.5f, 0f /* fraction*/); - mController.applyChangeInsets(new InsetsState()); + mController.applyChangeInsets(null /* outState */); assertEquals(0.5f, mController.getCurrentAlpha(), 0.5f - mController.getCurrentAlpha()); assertEquals(initialInsets, mController.getCurrentInsets()); } @@ -158,7 +160,7 @@ public class InsetsAnimationControlImplTest { @Test public void testChangeInsetsAndAlpha() { mController.setInsetsAndAlpha(Insets.of(0, 30, 40, 0), 0.5f, 1f); - mController.applyChangeInsets(new InsetsState()); + mController.applyChangeInsets(null /* outState */); assertEquals(0.5f, mController.getCurrentAlpha(), 0.5f - mController.getCurrentAlpha()); assertEquals(Insets.of(0, 30, 40, 0), mController.getCurrentInsets()); } diff --git a/core/tests/coretests/src/android/view/InsetsControllerTest.java b/core/tests/coretests/src/android/view/InsetsControllerTest.java index cc97eb5988e6..7b84f68c0f2c 100644 --- a/core/tests/coretests/src/android/view/InsetsControllerTest.java +++ b/core/tests/coretests/src/android/view/InsetsControllerTest.java @@ -683,71 +683,15 @@ public class InsetsControllerTest { @Test public void testRequestedState() { InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> { + final InsetsState state = mTestHost.getRequestedState(); - // The modified state can be controlled when we have control. - mController.onControlsChanged(createSingletonControl(ITYPE_STATUS_BAR)); - mController.hide(statusBars()); - assertFalse(mTestHost.getModifiedState().peekSource(ITYPE_STATUS_BAR).isVisible()); - - // The modified state won't be changed while losing control. - mController.onControlsChanged(null /* activeControls */); - assertFalse(mTestHost.getModifiedState().peekSource(ITYPE_STATUS_BAR).isVisible()); - - // The modified state won't be changed while state changed while we don't have control. - InsetsState newState = new InsetsState(mController.getState(), true /* copySource */); - mController.onStateChanged(newState); - assertFalse(mTestHost.getModifiedState().peekSource(ITYPE_STATUS_BAR).isVisible()); - - // The modified state won't be changed while controlling an insets without having the - // control. - mController.show(statusBars()); - assertFalse(mTestHost.getModifiedState().peekSource(ITYPE_STATUS_BAR).isVisible()); - - // The modified state can be updated while gaining control. - mController.onControlsChanged(createSingletonControl(ITYPE_STATUS_BAR)); - assertTrue(mTestHost.getModifiedState().peekSource(ITYPE_STATUS_BAR).isVisible()); - - // The modified state can still be updated if the local state and the requested state - // are the same. - mController.onControlsChanged(null /* activeControls */); - mController.hide(statusBars()); - newState = new InsetsState(mController.getState(), true /* copySource */); - newState.getSource(ITYPE_STATUS_BAR).setVisible(false); - mController.onStateChanged(newState); - mController.onControlsChanged(createSingletonControl(ITYPE_STATUS_BAR)); - assertFalse(mTestHost.getModifiedState().peekSource(ITYPE_STATUS_BAR).isVisible()); - - // The modified state will always be updated while receiving IME control if IME is - // requested visible. - mController.getSourceConsumer(ITYPE_IME).show(false /* fromIme */); - newState = new InsetsState(mController.getState(), true /* copySource */); - newState.getSource(ITYPE_IME).setVisible(true); - newState.getSource(ITYPE_IME).setFrame(1, 2, 3, 4); - mController.onStateChanged(newState); - mController.onControlsChanged(createSingletonControl(ITYPE_IME)); - assertEquals(newState.getSource(ITYPE_IME), - mTestHost.getModifiedState().peekSource(ITYPE_IME)); - newState = new InsetsState(mController.getState(), true /* copySource */); - newState.getSource(ITYPE_IME).setVisible(true); - newState.getSource(ITYPE_IME).setFrame(5, 6, 7, 8); - mController.onStateChanged(newState); - mController.onControlsChanged(createSingletonControl(ITYPE_IME)); - assertEquals(newState.getSource(ITYPE_IME), - mTestHost.getModifiedState().peekSource(ITYPE_IME)); - - // The modified frames cannot be updated if there is an animation. - mController.onControlsChanged(createSingletonControl(ITYPE_NAVIGATION_BAR)); - mController.hide(navigationBars()); - newState = new InsetsState(mController.getState(), true /* copySource */); - newState.getSource(ITYPE_NAVIGATION_BAR).getFrame().top--; - mController.onStateChanged(newState); - assertNotEquals(newState.getSource(ITYPE_NAVIGATION_BAR), - mTestHost.getModifiedState().peekSource(ITYPE_NAVIGATION_BAR)); + mController.hide(statusBars() | navigationBars()); + assertFalse(state.getSourceOrDefaultVisibility(ITYPE_STATUS_BAR)); + assertFalse(state.getSourceOrDefaultVisibility(ITYPE_NAVIGATION_BAR)); - // The modified frames can be updated while the animation is done. - mController.cancelExistingAnimations(); - assertEquals(newState.getSource(ITYPE_NAVIGATION_BAR), - mTestHost.getModifiedState().peekSource(ITYPE_NAVIGATION_BAR)); + mController.show(statusBars() | navigationBars()); + assertTrue(state.getSourceOrDefaultVisibility(ITYPE_STATUS_BAR)); + assertTrue(state.getSourceOrDefaultVisibility(ITYPE_NAVIGATION_BAR)); }); } @@ -878,7 +822,7 @@ public class InsetsControllerTest { public static class TestHost extends ViewRootInsetsControllerHost { - private InsetsState mModifiedState = new InsetsState(); + private final InsetsState mRequestedState = new InsetsState(); TestHost(ViewRootImpl viewRoot) { super(viewRoot); @@ -886,12 +830,12 @@ public class InsetsControllerTest { @Override public void onInsetsModified(InsetsState insetsState) { - mModifiedState = new InsetsState(insetsState, true /* copySource */); + mRequestedState.set(insetsState, true); super.onInsetsModified(insetsState); } - public InsetsState getModifiedState() { - return mModifiedState; + public InsetsState getRequestedState() { + return mRequestedState; } } } diff --git a/core/tests/coretests/src/android/view/ScrollCaptureClientTest.java b/core/tests/coretests/src/android/view/ScrollCaptureConnectionTest.java index e6ac2d6c43da..b9cf1e4a234c 100644 --- a/core/tests/coretests/src/android/view/ScrollCaptureClientTest.java +++ b/core/tests/coretests/src/android/view/ScrollCaptureConnectionTest.java @@ -47,11 +47,11 @@ import org.mockito.MockitoAnnotations; import org.mockito.stubbing.Answer; /** - * Tests of {@link ScrollCaptureClient}. + * Tests of {@link ScrollCaptureConnection}. */ @SuppressWarnings("UnnecessaryLocalVariable") @RunWith(AndroidJUnit4.class) -public class ScrollCaptureClientTest { +public class ScrollCaptureConnectionTest { private final Point mPositionInWindow = new Point(1, 2); private final Rect mLocalVisibleRect = new Rect(2, 3, 4, 5); @@ -63,7 +63,7 @@ public class ScrollCaptureClientTest { @Mock private Surface mSurface; @Mock - private IScrollCaptureController mClientCallbacks; + private IScrollCaptureCallbacks mConnectionCallbacks; @Mock private View mMockView1; @Mock @@ -86,8 +86,8 @@ public class ScrollCaptureClientTest { @Test public void testDelayedAction() { Runnable action = Mockito.mock(Runnable.class); - ScrollCaptureClient.DelayedAction delayed = - new ScrollCaptureClient.DelayedAction(mHandler, 100, action); + ScrollCaptureConnection.DelayedAction delayed = + new ScrollCaptureConnection.DelayedAction(mHandler, 100, action); try { Thread.sleep(200); } catch (InterruptedException ex) { @@ -103,8 +103,8 @@ public class ScrollCaptureClientTest { @Test public void testDelayedAction_cancel() { Runnable action = Mockito.mock(Runnable.class); - ScrollCaptureClient.DelayedAction delayed = - new ScrollCaptureClient.DelayedAction(mHandler, 100, action); + ScrollCaptureConnection.DelayedAction delayed = + new ScrollCaptureConnection.DelayedAction(mHandler, 100, action); try { Thread.sleep(50); } catch (InterruptedException ex) { @@ -125,8 +125,8 @@ public class ScrollCaptureClientTest { @Test public void testDelayedAction_timeoutNow() { Runnable action = Mockito.mock(Runnable.class); - ScrollCaptureClient.DelayedAction delayed = - new ScrollCaptureClient.DelayedAction(mHandler, 100, action); + ScrollCaptureConnection.DelayedAction delayed = + new ScrollCaptureConnection.DelayedAction(mHandler, 100, action); try { Thread.sleep(50); } catch (InterruptedException ex) { @@ -141,7 +141,7 @@ public class ScrollCaptureClientTest { /** Test creating a client with valid info */ @Test public void testConstruction() { - new ScrollCaptureClient(mTarget1, mClientCallbacks); + new ScrollCaptureConnection(mTarget1, mConnectionCallbacks); } /** Test creating a client fails if arguments are not valid. */ @@ -149,7 +149,7 @@ public class ScrollCaptureClientTest { public void testConstruction_requiresScrollBounds() { try { mTarget1.setScrollBounds(null); - new ScrollCaptureClient(mTarget1, mClientCallbacks); + new ScrollCaptureConnection(mTarget1, mConnectionCallbacks); fail("An exception was expected."); } catch (RuntimeException ex) { // Ignore, expected. @@ -174,48 +174,51 @@ public class ScrollCaptureClientTest { }; } - /** @see ScrollCaptureClient#startCapture(Surface) */ + /** @see ScrollCaptureConnection#startCapture(Surface) */ @Test public void testStartCapture() throws Exception { - final ScrollCaptureClient client = new ScrollCaptureClient(mTarget1, mClientCallbacks); + final ScrollCaptureConnection connection = new ScrollCaptureConnection(mTarget1, + mConnectionCallbacks); // Have the session start accepted immediately doAnswer(runRunnable(1)).when(mCallback1) .onScrollCaptureStart(any(ScrollCaptureSession.class), any(Runnable.class)); - client.startCapture(mSurface); + connection.startCapture(mSurface); getInstrumentation().waitForIdleSync(); verify(mCallback1, times(1)) .onScrollCaptureStart(any(ScrollCaptureSession.class), any(Runnable.class)); - verify(mClientCallbacks, times(1)).onCaptureStarted(); - verifyNoMoreInteractions(mClientCallbacks); + verify(mConnectionCallbacks, times(1)).onCaptureStarted(); + verifyNoMoreInteractions(mConnectionCallbacks); } @Test public void testStartCaptureTimeout() throws Exception { - final ScrollCaptureClient client = new ScrollCaptureClient(mTarget1, mClientCallbacks); - client.startCapture(mSurface); + final ScrollCaptureConnection connection = new ScrollCaptureConnection(mTarget1, + mConnectionCallbacks); + connection.startCapture(mSurface); // Force timeout to fire - client.getTimeoutAction().timeoutNow(); + connection.getTimeoutAction().timeoutNow(); getInstrumentation().waitForIdleSync(); verify(mCallback1, times(1)).onScrollCaptureEnd(any(Runnable.class)); } - private void startClient(ScrollCaptureClient client) throws Exception { + private void startCapture(ScrollCaptureConnection connection) throws Exception { doAnswer(runRunnable(1)).when(mCallback1) .onScrollCaptureStart(any(ScrollCaptureSession.class), any(Runnable.class)); - client.startCapture(mSurface); + connection.startCapture(mSurface); getInstrumentation().waitForIdleSync(); - reset(mCallback1, mClientCallbacks); + reset(mCallback1, mConnectionCallbacks); } - /** @see ScrollCaptureClient#requestImage(Rect) */ + /** @see ScrollCaptureConnection#requestImage(Rect) */ @Test public void testRequestImage() throws Exception { - final ScrollCaptureClient client = new ScrollCaptureClient(mTarget1, mClientCallbacks); - startClient(client); + final ScrollCaptureConnection connection = new ScrollCaptureConnection(mTarget1, + mConnectionCallbacks); + startCapture(connection); // Stub the callback to complete the request immediately doAnswer(reportBufferSent(/* sessionArg */ 0, /* frameNum */ 1L, new Rect(1, 2, 3, 4))) @@ -223,7 +226,7 @@ public class ScrollCaptureClientTest { .onScrollCaptureImageRequest(any(ScrollCaptureSession.class), any(Rect.class)); // Make the inbound binder call - client.requestImage(new Rect(1, 2, 3, 4)); + connection.requestImage(new Rect(1, 2, 3, 4)); // Wait for handler thread dispatch getInstrumentation().waitForIdleSync(); @@ -232,18 +235,20 @@ public class ScrollCaptureClientTest { // Wait for binder thread dispatch getInstrumentation().waitForIdleSync(); - verify(mClientCallbacks, times(1)).onCaptureBufferSent(eq(1L), eq(new Rect(1, 2, 3, 4))); + verify(mConnectionCallbacks, times(1)) + .onCaptureBufferSent(eq(1L), eq(new Rect(1, 2, 3, 4))); - verifyNoMoreInteractions(mCallback1, mClientCallbacks); + verifyNoMoreInteractions(mCallback1, mConnectionCallbacks); } @Test public void testRequestImageTimeout() throws Exception { - final ScrollCaptureClient client = new ScrollCaptureClient(mTarget1, mClientCallbacks); - startClient(client); + final ScrollCaptureConnection connection = new ScrollCaptureConnection(mTarget1, + mConnectionCallbacks); + startCapture(connection); // Make the inbound binder call - client.requestImage(new Rect(1, 2, 3, 4)); + connection.requestImage(new Rect(1, 2, 3, 4)); // Wait for handler thread dispatch getInstrumentation().waitForIdleSync(); @@ -251,20 +256,21 @@ public class ScrollCaptureClientTest { any(ScrollCaptureSession.class), eq(new Rect(1, 2, 3, 4))); // Force timeout to fire - client.getTimeoutAction().timeoutNow(); + connection.getTimeoutAction().timeoutNow(); getInstrumentation().waitForIdleSync(); // (callback not stubbed, does nothing) // Timeout triggers request to end capture verify(mCallback1, times(1)).onScrollCaptureEnd(any(Runnable.class)); - verifyNoMoreInteractions(mCallback1, mClientCallbacks); + verifyNoMoreInteractions(mCallback1, mConnectionCallbacks); } - /** @see ScrollCaptureClient#endCapture() */ + /** @see ScrollCaptureConnection#endCapture() */ @Test public void testEndCapture() throws Exception { - final ScrollCaptureClient client = new ScrollCaptureClient(mTarget1, mClientCallbacks); - startClient(client); + final ScrollCaptureConnection connection = new ScrollCaptureConnection(mTarget1, + mConnectionCallbacks); + startCapture(connection); // Stub the callback to complete the request immediately doAnswer(runRunnable(0)) @@ -272,7 +278,7 @@ public class ScrollCaptureClientTest { .onScrollCaptureEnd(any(Runnable.class)); // Make the inbound binder call - client.endCapture(); + connection.endCapture(); // Wait for handler thread dispatch getInstrumentation().waitForIdleSync(); @@ -280,30 +286,31 @@ public class ScrollCaptureClientTest { // Wait for binder thread dispatch getInstrumentation().waitForIdleSync(); - verify(mClientCallbacks, times(1)).onConnectionClosed(); + verify(mConnectionCallbacks, times(1)).onConnectionClosed(); - verifyNoMoreInteractions(mCallback1, mClientCallbacks); + verifyNoMoreInteractions(mCallback1, mConnectionCallbacks); } @Test public void testEndCaptureTimeout() throws Exception { - final ScrollCaptureClient client = new ScrollCaptureClient(mTarget1, mClientCallbacks); - startClient(client); + final ScrollCaptureConnection connection = new ScrollCaptureConnection(mTarget1, + mConnectionCallbacks); + startCapture(connection); // Make the inbound binder call - client.endCapture(); + connection.endCapture(); // Wait for handler thread dispatch getInstrumentation().waitForIdleSync(); verify(mCallback1, times(1)).onScrollCaptureEnd(any(Runnable.class)); // Force timeout to fire - client.getTimeoutAction().timeoutNow(); + connection.getTimeoutAction().timeoutNow(); // Wait for binder thread dispatch getInstrumentation().waitForIdleSync(); - verify(mClientCallbacks, times(1)).onConnectionClosed(); + verify(mConnectionCallbacks, times(1)).onConnectionClosed(); - verifyNoMoreInteractions(mCallback1, mClientCallbacks); + verifyNoMoreInteractions(mCallback1, mConnectionCallbacks); } } diff --git a/core/tests/coretests/src/android/widget/CustomInputConnectionEditText.java b/core/tests/coretests/src/android/widget/CustomInputConnectionEditText.java new file mode 100644 index 000000000000..2dfc53a591eb --- /dev/null +++ b/core/tests/coretests/src/android/widget/CustomInputConnectionEditText.java @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.widget; + +import android.content.Context; +import android.util.AttributeSet; +import android.util.Log; +import android.view.inputmethod.EditorInfo; +import android.view.inputmethod.InputConnection; +import android.view.inputmethod.InputConnectionWrapper; + +import java.util.Arrays; + +/** + * An {@link EditText} component that allows customizing its + * {@link android.view.inputmethod.InputConnection}. + */ +public class CustomInputConnectionEditText extends EditText { + private static final String LOG_TAG = "CustomInputConnectionEditText"; + + private String[] mContentMimeTypes; + private InputConnectionWrapper mInputConnectionWrapper; + + public CustomInputConnectionEditText(Context context) { + super(context); + } + + public CustomInputConnectionEditText(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public CustomInputConnectionEditText(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + public CustomInputConnectionEditText(Context context, AttributeSet attrs, int defStyleAttr, + int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + } + + public void setContentMimeTypes(String[] contentMimeTypes) { + mContentMimeTypes = contentMimeTypes; + } + + public void setInputConnectionWrapper(InputConnectionWrapper inputConnectionWrapper) { + mInputConnectionWrapper = inputConnectionWrapper; + } + + @Override + public InputConnection onCreateInputConnection(EditorInfo outAttrs) { + InputConnection ic = super.onCreateInputConnection(outAttrs); + if (ic == null) { + Log.d(LOG_TAG, "Not wrapping InputConnection, because super returned null"); + return null; + } + if (mInputConnectionWrapper == null) { + Log.d(LOG_TAG, "Not wrapping InputConnection, because wrapper is null"); + return ic; + } + + Log.d(LOG_TAG, "Wrapping InputConnection"); + mInputConnectionWrapper.setTarget(ic); + + Log.d(LOG_TAG, + "Setting EditorInfo.contentMimeTypes: " + Arrays.toString(mContentMimeTypes)); + outAttrs.contentMimeTypes = mContentMimeTypes; + + return mInputConnectionWrapper; + } +} diff --git a/core/tests/coretests/src/android/widget/CustomInputConnectionEditTextActivity.java b/core/tests/coretests/src/android/widget/CustomInputConnectionEditTextActivity.java new file mode 100644 index 000000000000..9328a50f900c --- /dev/null +++ b/core/tests/coretests/src/android/widget/CustomInputConnectionEditTextActivity.java @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.widget; + +import android.app.Activity; +import android.os.Bundle; + +import com.android.frameworks.coretests.R; + +/** + * Activity that uses an {@link EditText} component that allows customizing its + * {@link android.view.inputmethod.InputConnection}. + */ +public class CustomInputConnectionEditTextActivity extends Activity { + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_custom_input_connection_edit_text); + } +} diff --git a/core/tests/coretests/src/android/widget/TextViewOnReceiveContentCallbackTest.java b/core/tests/coretests/src/android/widget/TextViewOnReceiveContentCallbackTest.java new file mode 100644 index 000000000000..5112326ea0b6 --- /dev/null +++ b/core/tests/coretests/src/android/widget/TextViewOnReceiveContentCallbackTest.java @@ -0,0 +1,262 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.widget; + +import static android.view.OnReceiveContentCallback.Payload.SOURCE_AUTOFILL; +import static android.view.OnReceiveContentCallback.Payload.SOURCE_CLIPBOARD; +import static android.view.OnReceiveContentCallback.Payload.SOURCE_DRAG_AND_DROP; +import static android.view.OnReceiveContentCallback.Payload.SOURCE_INPUT_METHOD; +import static android.view.OnReceiveContentCallback.Payload.SOURCE_PROCESS_TEXT; +import static android.widget.TextViewOnReceiveContentCallback.canReuse; +import static android.widget.espresso.TextViewActions.clickOnTextAtIndex; + +import static androidx.test.espresso.Espresso.onView; +import static androidx.test.espresso.matcher.ViewMatchers.withId; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +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 android.app.Activity; +import android.app.Instrumentation; +import android.content.ClipData; +import android.content.ClipDescription; +import android.net.Uri; +import android.os.Bundle; +import android.util.ArraySet; +import android.view.OnReceiveContentCallback; +import android.view.inputmethod.InputConnection; +import android.view.inputmethod.InputConnectionWrapper; +import android.view.inputmethod.InputContentInfo; + +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.MediumTest; +import androidx.test.rule.ActivityTestRule; +import androidx.test.runner.AndroidJUnit4; + +import com.android.frameworks.coretests.R; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; + +/** + * Tests for {@link TextViewOnReceiveContentCallback}. Most of the test cases are in the CTS test + * {@link android.widget.cts.TextViewOnReceiveContentCallbackTest}. This class tests some internal + * implementation details, e.g. fallback to the keyboard image API. + */ +@RunWith(AndroidJUnit4.class) +@MediumTest +public class TextViewOnReceiveContentCallbackTest { + private static final Uri SAMPLE_CONTENT_URI = Uri.parse("content://com.example/path"); + + @Rule + public ActivityTestRule<CustomInputConnectionEditTextActivity> mActivityRule = + new ActivityTestRule<>(CustomInputConnectionEditTextActivity.class); + + private Instrumentation mInstrumentation; + private Activity mActivity; + private CustomInputConnectionEditText mEditText; + private TextViewOnReceiveContentCallback mDefaultCallback; + + @Before + public void before() { + mInstrumentation = InstrumentationRegistry.getInstrumentation(); + mActivity = mActivityRule.getActivity(); + mEditText = mActivity.findViewById(R.id.edittext2); + mDefaultCallback = mEditText.getEditorForTesting().getDefaultOnReceiveContentCallback(); + } + + @Test + public void testGetSupportedMimeTypes_fallbackToCommitContent() throws Throwable { + // Configure the EditText with an EditorInfo/InputConnection that supports some image MIME + // types. + mEditText.setContentMimeTypes(new String[] {"image/gif", "image/png"}); + MyInputConnection ic = new MyInputConnection(); + mEditText.setInputConnectionWrapper(ic); + + // Focus into the EditText. + onView(withId(mEditText.getId())).perform(clickOnTextAtIndex(0)); + + // Assert that the callback returns the MIME types declared in the EditorInfo in addition to + // the default. + assertThat(mDefaultCallback.getSupportedMimeTypes(mEditText)).containsExactly( + "text/*", "image/gif", "image/png"); + } + + @Test + public void testGetSupportedMimeTypes_fallbackToCommitContent_noMimeTypesInEditorInfo() + throws Throwable { + // Configure the EditText with an EditorInfo/InputConnection that doesn't declare any MIME + // types. + mEditText.setContentMimeTypes(new String[0]); + MyInputConnection ic = new MyInputConnection(); + mEditText.setInputConnectionWrapper(ic); + + // Focus into the EditText. + onView(withId(mEditText.getId())).perform(clickOnTextAtIndex(0)); + + // Assert that the callback returns the default MIME types. + assertThat(mDefaultCallback.getSupportedMimeTypes(mEditText)).containsExactly("text/*"); + } + + @Test + public void testOnReceive_fallbackToCommitContent() throws Throwable { + // Configure the EditText with an EditorInfo/InputConnection that supports some image MIME + // types. + mEditText.setContentMimeTypes(new String[] {"image/gif", "image/png"}); + MyInputConnection ic = new MyInputConnection(); + mEditText.setInputConnectionWrapper(ic); + + // Focus into the EditText. + onView(withId(mEditText.getId())).perform(clickOnTextAtIndex(0)); + + // Invoke the callback with SOURCE_AUTOFILL and assert that it triggers a call to + // InputConnection.commitContent. + ClipDescription description = new ClipDescription("", new String[] {"image/gif"}); + ClipData clip = new ClipData(description, new ClipData.Item(SAMPLE_CONTENT_URI)); + OnReceiveContentCallback.Payload payload = + new OnReceiveContentCallback.Payload.Builder(clip, SOURCE_AUTOFILL).build(); + mDefaultCallback.onReceiveContent(mEditText, payload); + verify(ic.mMock, times(1)) + .commitContent(any(InputContentInfo.class), eq(0), eq(null)); + verifyNoMoreInteractions(ic.mMock); + } + + @Test + public void testOnReceive_fallbackToCommitContent_noMimeTypesInEditorInfo() throws Throwable { + // Configure the EditText with an EditorInfo/InputConnection that doesn't declare any MIME + // types. + mEditText.setContentMimeTypes(new String[0]); + MyInputConnection ic = new MyInputConnection(); + mEditText.setInputConnectionWrapper(ic); + + // Focus into the EditText. + onView(withId(mEditText.getId())).perform(clickOnTextAtIndex(0)); + + // Invoke the callback and assert that the InputConnection is not invoked. + ClipDescription description = new ClipDescription("", new String[] {"image/gif"}); + ClipData clip = new ClipData(description, new ClipData.Item(SAMPLE_CONTENT_URI)); + OnReceiveContentCallback.Payload payload = + new OnReceiveContentCallback.Payload.Builder(clip, SOURCE_AUTOFILL).build(); + mDefaultCallback.onReceiveContent(mEditText, payload); + verifyZeroInteractions(ic.mMock); + } + + @Test + public void testOnReceive_fallbackToCommitContent_sourceOtherThanAutofill() throws Throwable { + // Configure the EditText with an EditorInfo/InputConnection that supports some image MIME + // types. + mEditText.setContentMimeTypes(new String[] {"image/gif", "image/png"}); + MyInputConnection ic = new MyInputConnection(); + mEditText.setInputConnectionWrapper(ic); + + // Focus into the EditText. + onView(withId(mEditText.getId())).perform(clickOnTextAtIndex(0)); + + // Invoke the callback with sources other than SOURCE_AUTOFILL and assert that it does NOT + // trigger calls to InputConnection.commitContent. + ClipDescription description = new ClipDescription("", new String[] {"image/gif"}); + ClipData clip = new ClipData(description, new ClipData.Item(SAMPLE_CONTENT_URI)); + OnReceiveContentCallback.Payload payload = + new OnReceiveContentCallback.Payload.Builder(clip, SOURCE_CLIPBOARD).build(); + mDefaultCallback.onReceiveContent(mEditText, payload); + verifyZeroInteractions(ic.mMock); + + payload = new OnReceiveContentCallback.Payload.Builder(clip, SOURCE_INPUT_METHOD).build(); + mDefaultCallback.onReceiveContent(mEditText, payload); + verifyZeroInteractions(ic.mMock); + + payload = new OnReceiveContentCallback.Payload.Builder(clip, SOURCE_DRAG_AND_DROP).build(); + mDefaultCallback.onReceiveContent(mEditText, payload); + verifyZeroInteractions(ic.mMock); + + payload = new OnReceiveContentCallback.Payload.Builder(clip, SOURCE_PROCESS_TEXT).build(); + mDefaultCallback.onReceiveContent(mEditText, payload); + verifyZeroInteractions(ic.mMock); + } + + @Test + public void testCanReuse() throws Throwable { + ArraySet<String> mimeTypes = null; + String[] editorContentMimeTypes = new String[0]; + assertThat(canReuse(mimeTypes, editorContentMimeTypes)).isFalse(); + + mimeTypes = new ArraySet<>(); + editorContentMimeTypes = new String[0]; + assertThat(canReuse(mimeTypes, editorContentMimeTypes)).isTrue(); + + mimeTypes = newArraySet("text/*"); + editorContentMimeTypes = new String[0]; + assertThat(canReuse(mimeTypes, editorContentMimeTypes)).isTrue(); + + mimeTypes = newArraySet("text/*"); + editorContentMimeTypes = new String[] {"text/*"}; + assertThat(canReuse(mimeTypes, editorContentMimeTypes)).isTrue(); + + mimeTypes = newArraySet("image/gif", "image/png", "text/*"); + editorContentMimeTypes = new String[] {"image/gif", "image/png"}; + assertThat(canReuse(mimeTypes, editorContentMimeTypes)).isTrue(); + + mimeTypes = newArraySet("image/gif", "image/png", "text/*"); + editorContentMimeTypes = new String[] {"image/gif", "image/png", "text/*"}; + assertThat(canReuse(mimeTypes, editorContentMimeTypes)).isTrue(); + + mimeTypes = newArraySet("image/gif", "image/png", "text/*"); + editorContentMimeTypes = new String[] {"image/gif"}; + assertThat(canReuse(mimeTypes, editorContentMimeTypes)).isFalse(); + + mimeTypes = newArraySet("image/gif", "image/png", "text/*"); + editorContentMimeTypes = new String[] {"image/gif", "image/png", "image/jpg"}; + assertThat(canReuse(mimeTypes, editorContentMimeTypes)).isFalse(); + + mimeTypes = newArraySet("image/gif", "image/png", "text/*"); + editorContentMimeTypes = new String[] {"image/gif", "image/jpg"}; + assertThat(canReuse(mimeTypes, editorContentMimeTypes)).isFalse(); + + mimeTypes = newArraySet("image/gif", "image/png", "text/*"); + editorContentMimeTypes = new String[] {"image/gif", "image/jpg", "text/*"}; + assertThat(canReuse(mimeTypes, editorContentMimeTypes)).isFalse(); + } + + private static class MyInputConnection extends InputConnectionWrapper { + public final InputConnection mMock; + + MyInputConnection() { + super(null, true); + mMock = Mockito.mock(InputConnection.class); + } + + @Override + public boolean commitContent(InputContentInfo inputContentInfo, int flags, Bundle opts) { + mMock.commitContent(inputContentInfo, flags, opts); + return true; + } + } + + @SafeVarargs + private static <T> ArraySet<T> newArraySet(T ... elements) { + return new ArraySet<>(elements); + } +} diff --git a/data/etc/car/Android.bp b/data/etc/car/Android.bp index e122e0026aac..745de84a3f04 100644 --- a/data/etc/car/Android.bp +++ b/data/etc/car/Android.bp @@ -149,3 +149,11 @@ prebuilt_etc { src: "com.android.car.ui.paintbooth.xml", filename_from_src: true, } + +prebuilt_etc { + name: "allowed_privapp_com.android.car.provision", + system_ext_specific: true, + sub_dir: "permissions", + src: "com.android.car.provision.xml", + filename_from_src: true, +}
\ No newline at end of file diff --git a/data/etc/car/com.android.car.provision.xml b/data/etc/car/com.android.car.provision.xml new file mode 100644 index 000000000000..fa51d55ee8a1 --- /dev/null +++ b/data/etc/car/com.android.car.provision.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2020 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> +<permissions> + <privapp-permissions package="com.android.car.provision"> + <permission name="android.permission.WRITE_SECURE_SETTINGS"/> + <permission name="android.permission.WRITE_SETTINGS"/> + </privapp-permissions> +</permissions> diff --git a/data/etc/car/com.google.android.car.kitchensink.xml b/data/etc/car/com.google.android.car.kitchensink.xml index 59aa45e1f6e4..3a20a9cb1695 100644 --- a/data/etc/car/com.google.android.car.kitchensink.xml +++ b/data/etc/car/com.google.android.car.kitchensink.xml @@ -44,5 +44,8 @@ <!-- use for CarServiceTest --> <permission name="android.permission.SET_ACTIVITY_WATCHER"/> <permission name="android.permission.WRITE_SECURE_SETTINGS"/> + + <!-- use for rotary fragment to enable/disable packages related to rotary --> + <permission name="android.permission.CHANGE_COMPONENT_ENABLED_STATE"/> </privapp-permissions> </permissions> diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json index 59727d5b2555..b19c8bfe1a6d 100644 --- a/data/etc/services.core.protolog.json +++ b/data/etc/services.core.protolog.json @@ -301,12 +301,6 @@ "group": "WM_DEBUG_ORIENTATION", "at": "com\/android\/server\/wm\/WindowState.java" }, - "-1741065110": { - "message": "No app is requesting an orientation, return %d for display id=%d", - "level": "VERBOSE", - "group": "WM_DEBUG_ORIENTATION", - "at": "com\/android\/server\/wm\/DisplayContent.java" - }, "-1730156332": { "message": "Display id=%d rotation changed to %d from %d, lastOrientation=%d", "level": "VERBOSE", @@ -529,6 +523,12 @@ "group": "WM_DEBUG_STATES", "at": "com\/android\/server\/wm\/Task.java" }, + "-1480772131": { + "message": "No app or window is requesting an orientation, return %d for display id=%d", + "level": "VERBOSE", + "group": "WM_DEBUG_ORIENTATION", + "at": "com\/android\/server\/wm\/DisplayContent.java" + }, "-1474292612": { "message": "Could not find task for id: %d", "level": "DEBUG", @@ -1405,12 +1405,6 @@ "group": "WM_DEBUG_RECENTS_ANIMATIONS", "at": "com\/android\/server\/wm\/RecentsAnimationController.java" }, - "-444624452": { - "message": "REPARENT from: %s to: %s", - "level": "INFO", - "group": "WM_SHOW_TRANSACTIONS", - "at": "com\/android\/server\/wm\/WindowSurfaceController.java" - }, "-443173857": { "message": "Moving pending starting from %s to %s", "level": "VERBOSE", @@ -1519,12 +1513,6 @@ "group": "WM_DEBUG_STATES", "at": "com\/android\/server\/wm\/Task.java" }, - "-324085783": { - "message": "SURFACE CROP %s: %s", - "level": "INFO", - "group": "WM_SHOW_TRANSACTIONS", - "at": "com\/android\/server\/wm\/WindowSurfaceController.java" - }, "-322743468": { "message": "setInputMethodInputTarget %s", "level": "INFO", @@ -1711,12 +1699,6 @@ "group": "WM_DEBUG_APP_TRANSITIONS_ANIM", "at": "com\/android\/server\/wm\/WindowContainer.java" }, - "-29233992": { - "message": "SURFACE CLEAR CROP: %s", - "level": "INFO", - "group": "WM_SHOW_TRANSACTIONS", - "at": "com\/android\/server\/wm\/WindowSurfaceController.java" - }, "-21399771": { "message": "activity %s already destroying, skipping request with reason:%s", "level": "VERBOSE", @@ -2515,12 +2497,6 @@ "group": "WM_ERROR", "at": "com\/android\/server\/wm\/WindowToken.java" }, - "845234215": { - "message": "App is requesting an orientation, return %d for display id=%d", - "level": "VERBOSE", - "group": "WM_DEBUG_ORIENTATION", - "at": "com\/android\/server\/wm\/DisplayContent.java" - }, "849147756": { "message": "Finish collecting in transition %d", "level": "VERBOSE", @@ -2683,6 +2659,12 @@ "group": "WM_ERROR", "at": "com\/android\/server\/wm\/WindowManagerService.java" }, + "1035154109": { + "message": "SURFACE backgroundBlur=%o: %s", + "level": "INFO", + "group": "WM_SHOW_TRANSACTIONS", + "at": "com\/android\/server\/wm\/WindowSurfaceController.java" + }, "1040675582": { "message": "Can't report activity configuration update - client not running, activityRecord=%s", "level": "WARN", @@ -2815,12 +2797,6 @@ "group": "WM_DEBUG_FOCUS", "at": "com\/android\/server\/wm\/WindowToken.java" }, - "1220075598": { - "message": "SURFACE SIZE %dx%d: %s", - "level": "INFO", - "group": "WM_SHOW_TRANSACTIONS", - "at": "com\/android\/server\/wm\/WindowSurfaceController.java" - }, "1224184681": { "message": "No longer Stopped: %s", "level": "VERBOSE", @@ -2923,6 +2899,12 @@ "group": "WM_DEBUG_IME", "at": "com\/android\/server\/wm\/ImeInsetsSourceProvider.java" }, + "1381227466": { + "message": "App is requesting an orientation, return %d for display id=%d", + "level": "VERBOSE", + "group": "WM_DEBUG_ORIENTATION", + "at": "com\/android\/server\/wm\/TaskDisplayArea.java" + }, "1401295262": { "message": "Mode default, asking user", "level": "WARN", @@ -3133,6 +3115,18 @@ "group": "WM_DEBUG_RESIZE", "at": "com\/android\/server\/wm\/WindowState.java" }, + "1640436199": { + "message": "No app is requesting an orientation, return %d for display id=%d", + "level": "VERBOSE", + "group": "WM_DEBUG_ORIENTATION", + "at": "com\/android\/server\/wm\/TaskDisplayArea.java" + }, + "1648338379": { + "message": "Display id=%d is ignoring all orientation requests, return %d", + "level": "VERBOSE", + "group": "WM_DEBUG_ORIENTATION", + "at": "com\/android\/server\/wm\/DisplayContent.java" + }, "1653210583": { "message": "Removing app %s delayed=%b animation=%s animating=%b", "level": "VERBOSE", diff --git a/errorprone/java/com/google/errorprone/bugpatterns/android/EfficientStringsChecker.java b/errorprone/java/com/google/errorprone/bugpatterns/android/EfficientStringsChecker.java new file mode 100644 index 000000000000..3fbd51deec65 --- /dev/null +++ b/errorprone/java/com/google/errorprone/bugpatterns/android/EfficientStringsChecker.java @@ -0,0 +1,146 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.errorprone.bugpatterns.android; + +import static com.google.errorprone.BugPattern.SeverityLevel.WARNING; +import static com.google.errorprone.matchers.Matchers.allOf; +import static com.google.errorprone.matchers.Matchers.contains; +import static com.google.errorprone.matchers.Matchers.kindIs; +import static com.google.errorprone.matchers.Matchers.methodInvocation; +import static com.google.errorprone.matchers.Matchers.not; +import static com.google.errorprone.matchers.Matchers.staticMethod; + +import com.google.auto.service.AutoService; +import com.google.errorprone.BugPattern; +import com.google.errorprone.VisitorState; +import com.google.errorprone.bugpatterns.BugChecker; +import com.google.errorprone.bugpatterns.BugChecker.MethodInvocationTreeMatcher; +import com.google.errorprone.matchers.Description; +import com.google.errorprone.matchers.Matcher; +import com.google.errorprone.predicates.TypePredicate; +import com.google.errorprone.util.ASTHelpers; +import com.sun.source.tree.ExpressionTree; +import com.sun.source.tree.LiteralTree; +import com.sun.source.tree.MethodInvocationTree; +import com.sun.source.tree.Tree.Kind; +import com.sun.tools.javac.code.Symbol.VarSymbol; +import com.sun.tools.javac.code.Type; + +import java.util.List; + +/** + * Android offers several efficient alternatives to some upstream {@link String} + * operations. + */ +@AutoService(BugChecker.class) +@BugPattern( + name = "AndroidFrameworkEfficientStrings", + summary = "Verifies efficient Strings best-practices", + severity = WARNING) +public final class EfficientStringsChecker extends BugChecker + implements MethodInvocationTreeMatcher { + + private static final Matcher<ExpressionTree> FORMAT_CALL = methodInvocation( + staticMethod().onClass("java.lang.String").named("format")); + private static final Matcher<ExpressionTree> PRECONDITIONS_CALL = methodInvocation( + staticMethod().onClass(withSimpleName("Preconditions")).withAnyName()); + private static final Matcher<ExpressionTree> OBJECTS_CALL = methodInvocation( + staticMethod().onClass("java.util.Objects").named("requireNonNull")); + + /** + * Match an expression which combines both string literals any other dynamic + * values, since these allocate a transparent StringBuilder. + * <p> + * This won't match a single isolated string literal, or a chain consisting + * of only string literals, since those don't require dynamic construction. + */ + private static final Matcher<ExpressionTree> CONTAINS_DYNAMIC_STRING = allOf( + contains(ExpressionTree.class, kindIs(Kind.STRING_LITERAL)), + contains(ExpressionTree.class, not(kindIs(Kind.STRING_LITERAL)))); + + @Override + public Description matchMethodInvocation(MethodInvocationTree tree, VisitorState state) { + if (FORMAT_CALL.matches(tree, state)) { + // Skip over possible locale to find format string + final List<? extends ExpressionTree> args = tree.getArguments(); + final ExpressionTree formatArg; + final List<VarSymbol> vars = ASTHelpers.getSymbol(tree).params(); + if (vars.get(0).type.toString().equals("java.util.Locale")) { + formatArg = args.get(1); + } else { + formatArg = args.get(0); + } + + // Determine if format string is "simple" enough to replace + if (formatArg.getKind() == Kind.STRING_LITERAL) { + final String format = String.valueOf(((LiteralTree) formatArg).getValue()); + if (isSimple(format)) { + return buildDescription(formatArg) + .setMessage("Simple format strings can be replaced with " + + "TextUtils.formatSimple() for a 6x performance improvement") + .build(); + } + } + } else if (PRECONDITIONS_CALL.matches(tree, state) + || OBJECTS_CALL.matches(tree, state)) { + final List<? extends ExpressionTree> args = tree.getArguments(); + for (int i = 1 ; i < args.size(); i++) { + final ExpressionTree arg = args.get(i); + if (CONTAINS_DYNAMIC_STRING.matches(arg, state)) { + return buildDescription(arg) + .setMessage("Building dynamic messages is discouraged, since they " + + "always allocate a transparent StringBuilder, even in " + + "the successful case") + .build(); + } + } + } + return Description.NO_MATCH; + } + + static boolean isSimple(String format) { + for (int i = 0; i < format.length(); i++) { + char c = format.charAt(i); + if (c == '%') { + i++; + c = format.charAt(i); + switch (c) { + case 'b': + case 'c': + case 'd': + case 'f': + case 's': + case 'x': + case '%': + break; + default: + return false; + } + } + } + return true; + } + + static TypePredicate withSimpleName(final String filter) { + return new TypePredicate() { + @Override + public boolean apply(Type type, VisitorState state) { + return type.tsym.getSimpleName().toString().equals(filter); + } + }; + } +} diff --git a/errorprone/tests/java/com/google/errorprone/bugpatterns/android/EfficientStringsCheckerTest.java b/errorprone/tests/java/com/google/errorprone/bugpatterns/android/EfficientStringsCheckerTest.java new file mode 100644 index 000000000000..a755564d52dd --- /dev/null +++ b/errorprone/tests/java/com/google/errorprone/bugpatterns/android/EfficientStringsCheckerTest.java @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.errorprone.bugpatterns.android; + +import static junit.framework.Assert.assertFalse; +import static junit.framework.Assert.assertTrue; + +import com.google.errorprone.CompilationTestHelper; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +@RunWith(JUnit4.class) +public class EfficientStringsCheckerTest { + private CompilationTestHelper compilationHelper; + + @Before + public void setUp() { + compilationHelper = CompilationTestHelper.newInstance( + EfficientStringsChecker.class, getClass()); + } + + @Test + public void testSimple() { + assertTrue(EfficientStringsChecker.isSimple("")); + assertTrue(EfficientStringsChecker.isSimple("%s")); + assertTrue(EfficientStringsChecker.isSimple("String %s%s and %%%% number %d%d together")); + + assertFalse(EfficientStringsChecker.isSimple("%04d")); + assertFalse(EfficientStringsChecker.isSimple("%02x:%02x:%02x")); + } + + @Test + public void testFormat() { + compilationHelper + .addSourceLines("Example.java", + "import java.util.Locale;", + "public class Example {", + " public void example(String str) {", + " String.format(str, str);", + " // BUG: Diagnostic contains:", + " String.format(\"foo %s bar\", str);", + " // BUG: Diagnostic contains:", + " String.format(\"foo %d bar\", 42);", + " String.format(\"foo %04d bar\", 42);", + " }", + " public void exampleLocale(String str) {", + " String.format(Locale.US, str, str);", + " // BUG: Diagnostic contains:", + " String.format(Locale.US, \"foo %s bar\", str);", + " // BUG: Diagnostic contains:", + " String.format(Locale.US, \"foo %d bar\", 42);", + " String.format(Locale.US, \"foo %04d bar\", 42);", + " }", + "}") + .doTest(); + } + + @Test + public void testPreconditions() { + compilationHelper + .addSourceFile("/android/util/Preconditions.java") + .addSourceLines("Example.java", + "import android.util.Preconditions;", + "import java.util.Objects;", + "public class Example {", + " String str;", + " public void checkState(boolean val) {", + " Preconditions.checkState(val);", + " Preconditions.checkState(val, str);", + " Preconditions.checkState(val, \"foo\");", + " Preconditions.checkState(val, \"foo\" + \"bar\");", + " // BUG: Diagnostic contains:", + " Preconditions.checkState(val, \"foo \" + val);", + " }", + " public void checkArgument(boolean val) {", + " Preconditions.checkArgument(val);", + " Preconditions.checkArgument(val, str);", + " Preconditions.checkArgument(val, \"foo\");", + " Preconditions.checkArgument(val, \"foo\" + \"bar\");", + " // BUG: Diagnostic contains:", + " Preconditions.checkArgument(val, \"foo \" + val);", + " }", + " public void checkNotNull(Object val) {", + " Preconditions.checkNotNull(val);", + " Preconditions.checkNotNull(val, str);", + " Preconditions.checkNotNull(val, \"foo\");", + " Preconditions.checkNotNull(val, \"foo\" + \"bar\");", + " // BUG: Diagnostic contains:", + " Preconditions.checkNotNull(val, \"foo \" + val);", + " }", + " public void requireNonNull(Object val) {", + " Objects.requireNonNull(val);", + " Objects.requireNonNull(val, str);", + " Objects.requireNonNull(val, \"foo\");", + " Objects.requireNonNull(val, \"foo\" + \"bar\");", + " // BUG: Diagnostic contains:", + " Objects.requireNonNull(val, \"foo \" + val);", + " }", + "}") + .doTest(); + } +} diff --git a/errorprone/tests/res/android/util/Preconditions.java b/errorprone/tests/res/android/util/Preconditions.java new file mode 100644 index 000000000000..558cdaf483a4 --- /dev/null +++ b/errorprone/tests/res/android/util/Preconditions.java @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.util; + +public class Preconditions { + public static void checkArgument(boolean expression) { + throw new UnsupportedOperationException(); + } + + public static void checkArgument(boolean expression, Object errorMessage) { + throw new UnsupportedOperationException(); + } + + public static <T> T checkNotNull(T reference) { + throw new UnsupportedOperationException(); + } + + public static <T> T checkNotNull(T reference, Object errorMessage) { + throw new UnsupportedOperationException(); + } + + public static void checkState(boolean expression) { + throw new UnsupportedOperationException(); + } + + public static void checkState(boolean expression, String message) { + throw new UnsupportedOperationException(); + } +} diff --git a/graphics/java/android/graphics/BLASTBufferQueue.java b/graphics/java/android/graphics/BLASTBufferQueue.java index 4c0f890eee40..8284042c4dca 100644 --- a/graphics/java/android/graphics/BLASTBufferQueue.java +++ b/graphics/java/android/graphics/BLASTBufferQueue.java @@ -32,6 +32,7 @@ public final class BLASTBufferQueue { private static native Surface nativeGetSurface(long ptr); private static native void nativeSetNextTransaction(long ptr, long transactionPtr); private static native void nativeUpdate(long ptr, long surfaceControl, long width, long height); + private static native void nativeFlushShadowQueue(long ptr); /** Create a new connection with the surface flinger. */ public BLASTBufferQueue(String name, SurfaceControl sc, int width, int height, @@ -69,4 +70,8 @@ public final class BLASTBufferQueue { super.finalize(); } } + + public void flushShadowQueue() { + nativeFlushShadowQueue(mNativeObject); + } } diff --git a/graphics/java/android/graphics/BaseCanvas.java b/graphics/java/android/graphics/BaseCanvas.java index 05df250fa6b9..54f9fa81b9b7 100644 --- a/graphics/java/android/graphics/BaseCanvas.java +++ b/graphics/java/android/graphics/BaseCanvas.java @@ -26,11 +26,13 @@ import android.compat.annotation.UnsupportedAppUsage; import android.graphics.Canvas.VertexMode; import android.graphics.fonts.Font; import android.graphics.text.MeasuredText; +import android.graphics.text.TextRunShaper; import android.text.GraphicsOperations; import android.text.MeasuredParagraph; import android.text.PrecomputedText; import android.text.SpannableString; import android.text.SpannedString; +import android.text.TextShaper; import android.text.TextUtils; import com.android.internal.util.Preconditions; @@ -471,8 +473,8 @@ public abstract class BaseCanvas { * @param font Font used for drawing. * @param paint Paint used for drawing. The typeface set to this paint is ignored. * - * @see android.graphics.text.TextShaper - * @see android.text.StyledTextShaper + * @see TextRunShaper + * @see TextShaper */ public void drawGlyphs( @NonNull int[] glyphIds, diff --git a/graphics/java/android/graphics/Canvas.java b/graphics/java/android/graphics/Canvas.java index 5e07d156a06a..829d0f4be513 100644 --- a/graphics/java/android/graphics/Canvas.java +++ b/graphics/java/android/graphics/Canvas.java @@ -26,7 +26,9 @@ import android.annotation.Size; import android.compat.annotation.UnsupportedAppUsage; import android.graphics.fonts.Font; import android.graphics.text.MeasuredText; +import android.graphics.text.TextRunShaper; import android.os.Build; +import android.text.TextShaper; import dalvik.annotation.optimization.CriticalNative; import dalvik.annotation.optimization.FastNative; @@ -2053,7 +2055,7 @@ public class Canvas extends BaseCanvas { * Draw array of glyphs with specified font. * * @param glyphIds Array of glyph IDs. The length of array must be greater than or equal to - * {@code glyphStart + glyphCount}. + * {@code glyphIdOffset + glyphCount}. * @param glyphIdOffset Number of elements to skip before drawing in <code>glyphIds</code> * array. * @param positions A flattened X and Y position array. The first glyph X position must be @@ -2071,8 +2073,8 @@ public class Canvas extends BaseCanvas { * @param font Font used for drawing. * @param paint Paint used for drawing. The typeface set to this paint is ignored. * - * @see android.graphics.text.TextShaper - * @see android.text.StyledTextShaper + * @see TextRunShaper + * @see TextShaper */ public void drawGlyphs( @NonNull int[] glyphIds, diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java index 28d7911c771f..a191fe56b31d 100644 --- a/graphics/java/android/graphics/Paint.java +++ b/graphics/java/android/graphics/Paint.java @@ -115,6 +115,21 @@ public class Paint { Align.LEFT, Align.CENTER, Align.RIGHT }; + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(flag = true, value = { + ANTI_ALIAS_FLAG, + FILTER_BITMAP_FLAG, + DITHER_FLAG, + UNDERLINE_TEXT_FLAG, + STRIKE_THRU_TEXT_FLAG, + FAKE_BOLD_TEXT_FLAG, + LINEAR_TEXT_FLAG, + SUBPIXEL_TEXT_FLAG, + EMBEDDED_BITMAP_TEXT_FLAG + }) + public @interface PaintFlag{} + /** * Paint flag that enables antialiasing when drawing. * @@ -724,7 +739,7 @@ public class Paint { * * @return the paint's flags (see enums ending in _Flag for bit masks) */ - public int getFlags() { + public @PaintFlag int getFlags() { return nGetFlags(mNativePaint); } @@ -733,7 +748,7 @@ public class Paint { * * @param flags The new flag bits for the paint */ - public void setFlags(int flags) { + public void setFlags(@PaintFlag int flags) { nSetFlags(mNativePaint, flags); } diff --git a/graphics/java/android/graphics/Shader.java b/graphics/java/android/graphics/Shader.java index d71ff1138b25..7651d019bdcb 100644 --- a/graphics/java/android/graphics/Shader.java +++ b/graphics/java/android/graphics/Shader.java @@ -83,21 +83,22 @@ public class Shader { public enum TileMode { /** - * replicate the edge color if the shader draws outside of its - * original bounds + * Replicate the edge color if the shader draws outside of its + * original bounds. */ CLAMP (0), /** - * repeat the shader's image horizontally and vertically + * Repeat the shader's image horizontally and vertically. */ REPEAT (1), /** - * repeat the shader's image horizontally and vertically, alternating - * mirror images so that adjacent images always seam + * Repeat the shader's image horizontally and vertically, alternating + * mirror images so that adjacent images always seam. */ MIRROR(2), /** - * Only draw within the original domain, return transparent-black everywhere else + * Render the shader's image pixels only within its original bounds. If the shader + * draws outside of its original bounds, transparent black is drawn instead. */ DECAL(3); diff --git a/graphics/java/android/graphics/fonts/Font.java b/graphics/java/android/graphics/fonts/Font.java index 21b8fc6f1c80..586c512b3f97 100644 --- a/graphics/java/android/graphics/fonts/Font.java +++ b/graphics/java/android/graphics/fonts/Font.java @@ -523,6 +523,9 @@ public final class Font { /** * Returns a font file buffer. * + * Duplicate before reading values by {@link ByteBuffer#duplicate()} for avoiding unexpected + * reading position sharing. + * * @return a font buffer */ public @NonNull ByteBuffer getBuffer() { @@ -628,18 +631,49 @@ public final class Font { if (o == this) { return true; } - if (o == null || !(o instanceof Font)) { + if (!(o instanceof Font)) { return false; } Font f = (Font) o; - return mFontStyle.equals(f.mFontStyle) && f.mTtcIndex == mTtcIndex - && Arrays.equals(f.mAxes, mAxes) && f.mBuffer.equals(mBuffer) - && Objects.equals(f.mLocaleList, mLocaleList); + boolean paramEqual = mFontStyle.equals(f.mFontStyle) && f.mTtcIndex == mTtcIndex + && Arrays.equals(f.mAxes, mAxes) && Objects.equals(f.mLocaleList, mLocaleList) + && Objects.equals(mFile, f.mFile); + + if (!paramEqual) { + return false; + } + + // Shortcut for different font buffer check by comparing size. + if (mBuffer.capacity() != f.mBuffer.capacity()) { + return false; + } + + // ByteBuffer#equals compares all bytes which is not performant for e.g HashMap. Since + // underlying native font object holds buffer address, check if this buffer points exactly + // the same address as a shortcut of equality. For being compatible with of API30 or before, + // check buffer position even if the buffer points the same address. + if (nIsSameBufferAddress(mNativePtr, f.mNativePtr) + && mBuffer.position() == f.mBuffer.position()) { + return true; + } + + // Unfortunately, need to compare bytes one-by-one since the buffer may be different font + // file but has the same file size, or two font has same content but they are allocated + // differently. For being compatible with API30 ore before, compare with ByteBuffer#equals. + return mBuffer.equals(f.mBuffer); } @Override public int hashCode() { - return Objects.hash(mFontStyle, mTtcIndex, Arrays.hashCode(mAxes), mBuffer, mLocaleList); + return Objects.hash( + mFontStyle, + mTtcIndex, + Arrays.hashCode(mAxes), + // Use Buffer size instead of ByteBuffer#hashCode since ByteBuffer#hashCode traverse + // data which is not performant e.g. for HashMap. The hash collision are less likely + // happens because it is unlikely happens the different font files has exactly the + // same size. + mLocaleList); } @Override @@ -687,7 +721,9 @@ public final class Font { charBuffer[3] = (char) ((packedAxis & 0x0000_00FF_0000_0000L) >> 32); axes[i] = new FontVariationAxis(new String(charBuffer), value); } - Font.Builder builder = new Font.Builder(buffer) + String path = nGetFontPath(ptr); + File file = (path == null) ? null : new File(path); + Font.Builder builder = new Font.Builder(buffer, file, "") .setWeight(weight) .setSlant(italic ? FontStyle.FONT_SLANT_ITALIC : FontStyle.FONT_SLANT_UPRIGHT) .setTtcIndex(ttcIndex) @@ -712,6 +748,9 @@ public final class Font { private static native long nGetAxisInfo(long ptr, int i); @FastNative + private static native String nGetFontPath(long ptr); + + @FastNative private static native float nGetGlyphBounds(long font, int glyphId, long paint, RectF rect); @FastNative @@ -719,4 +758,7 @@ public final class Font { @CriticalNative private static native long nGetNativeFontPtr(long ptr); + + @CriticalNative + private static native boolean nIsSameBufferAddress(long lFontPtr, long rFontPtr); } diff --git a/graphics/java/android/graphics/text/GlyphStyle.java b/graphics/java/android/graphics/text/GlyphStyle.java deleted file mode 100644 index cc8c4d26fb5e..000000000000 --- a/graphics/java/android/graphics/text/GlyphStyle.java +++ /dev/null @@ -1,234 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.graphics.text; - -import android.annotation.ColorInt; -import android.annotation.FloatRange; -import android.annotation.NonNull; -import android.graphics.Paint; - -import java.util.Objects; - -/** - * Represents subset of Paint parameters such as font size, scaleX that is used to draw a glyph. - * - * Glyph is a most primitive unit of text drawing. - * - */ -public class GlyphStyle { - private @ColorInt int mColor; - private float mFontSize; - private float mScaleX; - private float mSkewX; - private int mFlags; - - /** - * @param color a color. - * @param fontSize a font size in pixels. - * @param scaleX a horizontal scale factor. - * @param skewX a horizontal skew factor - * @param flags paint flags - * - * @see Paint#getFlags() - * @see Paint#setFlags(int) - */ - public GlyphStyle( - @ColorInt int color, - @FloatRange(from = 0) float fontSize, - @FloatRange(from = 0) float scaleX, - @FloatRange(from = 0) float skewX, - int flags) { - mColor = color; - mFontSize = fontSize; - mScaleX = scaleX; - mSkewX = skewX; - mFlags = flags; - } - - /** - * Create glyph style from Paint - * - * @param paint a paint - */ - public GlyphStyle(@NonNull Paint paint) { - setFromPaint(paint); - } - - /** - * Gets the color. - * - * @return a color - * @see Paint#getColor() - * @see Paint#setColor(int) - */ - public @ColorInt int getColor() { - return mColor; - } - - /** - * Sets the color. - * - * @param color a color - * @see Paint#getColor() - * @see Paint#setColor(int) - */ - public void setColor(@ColorInt int color) { - mColor = color; - } - - /** - * Gets the font size in pixels. - * - * @return font size - * @see Paint#getTextSize() - * @see Paint#setTextSize(float) - */ - public @FloatRange(from = 0) float getFontSize() { - return mFontSize; - } - - /** - * Sets the font size in pixels. - * - * @param fontSize font size in pixel - * @see Paint#getTextSize() - * @see Paint#setTextSize(float) - */ - public void setFontSize(@FloatRange(from = 0) float fontSize) { - mFontSize = fontSize; - } - - /** - * Return the horizontal scale factor - * - * @return a horizontal scale factor - * @see Paint#getTextScaleX() - * @see Paint#setTextScaleX(float) - */ - public @FloatRange(from = 0) float getScaleX() { - return mScaleX; - } - - /** - * Set the horizontal scale factor - * - * @param scaleX a horizontal scale factor - * @see Paint#getTextScaleX() - * @see Paint#setTextScaleX(float) - */ - public void setScaleX(@FloatRange(from = 0) float scaleX) { - mScaleX = scaleX; - } - - /** - * Return the horizontal skew factor - * - * @return a horizontal skew factor - * @see Paint#getTextSkewX() - * @see Paint#setTextSkewX(float) - */ - public @FloatRange(from = 0) float getSkewX() { - return mSkewX; - } - - /** - * Set the horizontal skew factor - * - * @param skewX a horizontal skew factor - * @see Paint#getTextSkewX() - * @see Paint#setTextSkewX(float) - */ - public void setSkewX(@FloatRange(from = 0) float skewX) { - mSkewX = skewX; - } - - /** - * Returns the Paint flags. - * - * @return a paint flags - * @see Paint#getFlags() - * @see Paint#setFlags(int) - */ - public int getFlags() { - return mFlags; - } - - /** - * Set the Paint flags. - * - * @param flags a paint flags - * @see Paint#getFlags() - * @see Paint#setFlags(int) - */ - public void setFlags(int flags) { - mFlags = flags; - } - - /** - * Applies glyph style to the paint object. - * - * @param paint a paint object - */ - public void applyToPaint(@NonNull Paint paint) { - paint.setColor(mColor); - paint.setTextSize(mFontSize); - paint.setTextScaleX(mScaleX); - paint.setTextSkewX(mSkewX); - paint.setFlags(mFlags); - } - - /** - * Copy parameters from a Paint object. - * - * @param paint a paint object - */ - public void setFromPaint(@NonNull Paint paint) { - mColor = paint.getColor(); - mFontSize = paint.getTextSize(); - mScaleX = paint.getTextScaleX(); - mSkewX = paint.getTextSkewX(); - mFlags = paint.getFlags(); - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (!(o instanceof GlyphStyle)) return false; - GlyphStyle that = (GlyphStyle) o; - return that.mColor == mColor - && Float.compare(that.mFontSize, mFontSize) == 0 - && Float.compare(that.mScaleX, mScaleX) == 0 - && Float.compare(that.mSkewX, mSkewX) == 0 - && mFlags == that.mFlags; - } - - @Override - public int hashCode() { - return Objects.hash(mColor, mFontSize, mScaleX, mSkewX, mFlags); - } - - @Override - public String toString() { - return "GlyphStyle{" - + "mColor=" + mColor - + ", mFontSize=" + mFontSize - + ", mScaleX=" + mScaleX - + ", mSkewX=" + mSkewX - + ", mFlags=" + mFlags - + '}'; - } -} diff --git a/graphics/java/android/graphics/text/PositionedGlyphs.java b/graphics/java/android/graphics/text/PositionedGlyphs.java index 7364d545a452..ecbc45c54d85 100644 --- a/graphics/java/android/graphics/text/PositionedGlyphs.java +++ b/graphics/java/android/graphics/text/PositionedGlyphs.java @@ -35,11 +35,12 @@ import java.util.Objects; * Text shaping result object for single style text. * * You can get text shaping result by - * {@link TextShaper#shapeTextRun(char[], int, int, int, int, float, float, boolean, Paint)} and - * {@link TextShaper#shapeTextRun(CharSequence, int, int, int, int, float, float, boolean, Paint)}. + * {@link TextRunShaper#shapeTextRun(char[], int, int, int, int, float, float, boolean, Paint)} and + * {@link TextRunShaper#shapeTextRun(CharSequence, int, int, int, int, float, float, boolean, + * Paint)}. * - * @see TextShaper#shapeTextRun(char[], int, int, int, int, float, float, boolean, Paint) - * @see TextShaper#shapeTextRun(CharSequence, int, int, int, int, float, float, boolean, Paint) + * @see TextRunShaper#shapeTextRun(char[], int, int, int, int, float, float, boolean, Paint) + * @see TextRunShaper#shapeTextRun(CharSequence, int, int, int, int, float, float, boolean, Paint) */ public final class PositionedGlyphs { private static final NativeAllocationRegistry REGISTRY = @@ -49,7 +50,6 @@ public final class PositionedGlyphs { private final long mLayoutPtr; private final float mXOffset; private final float mYOffset; - private final GlyphStyle mGlyphStyle; private final ArrayList<Font> mFonts; /** @@ -62,7 +62,7 @@ public final class PositionedGlyphs { * * @return total amount of advance */ - public float getTotalAdvance() { + public float getAdvance() { return nGetTotalAdvance(mLayoutPtr); } @@ -91,21 +91,11 @@ public final class PositionedGlyphs { } /** - * Returns the glyph style used for drawing the glyph at the given index. - * - * @return A glyph style - */ - @NonNull - public GlyphStyle getStyle() { - return mGlyphStyle; - } - - /** * Returns the amount of X offset added to glyph position. * * @return The X offset added to glyph position. */ - public float getOriginX() { + public float getOffsetX() { return mXOffset; } @@ -114,7 +104,7 @@ public final class PositionedGlyphs { * * @return The Y offset added to glyph position. */ - public float getOriginY() { + public float getOffsetY() { return mYOffset; } @@ -144,7 +134,7 @@ public final class PositionedGlyphs { * Returns the glyph ID used for drawing the glyph at the given index. * * @param index the glyph index - * @return A font object + * @return An glyph ID of the font. */ @IntRange(from = 0) public int getGlyphId(@IntRange(from = 0) int index) { @@ -158,7 +148,7 @@ public final class PositionedGlyphs { * @param index the glyph index * @return A X offset in pixels */ - public float getPositionX(@IntRange(from = 0) int index) { + public float getGlyphX(@IntRange(from = 0) int index) { Preconditions.checkArgumentInRange(index, 0, glyphCount() - 1, "index"); return nGetX(mLayoutPtr, index) + mXOffset; } @@ -169,7 +159,7 @@ public final class PositionedGlyphs { * @param index the glyph index * @return A Y offset in pixels. */ - public float getPositionY(@IntRange(from = 0) int index) { + public float getGlyphY(@IntRange(from = 0) int index) { Preconditions.checkArgumentInRange(index, 0, glyphCount() - 1, "index"); return nGetY(mLayoutPtr, index) + mYOffset; } @@ -184,7 +174,6 @@ public final class PositionedGlyphs { */ public PositionedGlyphs(long layoutPtr, @NonNull Paint paint, float xOffset, float yOffset) { mLayoutPtr = layoutPtr; - mGlyphStyle = new GlyphStyle(paint); int glyphCount = nGetGlyphCount(layoutPtr); mFonts = new ArrayList<>(glyphCount); mXOffset = xOffset; @@ -229,14 +218,13 @@ public final class PositionedGlyphs { if (!(o instanceof PositionedGlyphs)) return false; PositionedGlyphs that = (PositionedGlyphs) o; - if (!mGlyphStyle.equals(that.mGlyphStyle)) return false; if (mXOffset != that.mXOffset || mYOffset != that.mYOffset) return false; if (glyphCount() != that.glyphCount()) return false; for (int i = 0; i < glyphCount(); ++i) { if (getGlyphId(i) != that.getGlyphId(i)) return false; - if (getPositionX(i) != that.getPositionX(i)) return false; - if (getPositionY(i) != that.getPositionY(i)) return false; + if (getGlyphX(i) != that.getGlyphX(i)) return false; + if (getGlyphY(i) != that.getGlyphY(i)) return false; // Intentionally using reference equality since font equality is heavy due to buffer // compare. if (getFont(i) != that.getFont(i)) return false; @@ -247,10 +235,10 @@ public final class PositionedGlyphs { @Override public int hashCode() { - int hashCode = Objects.hash(mXOffset, mYOffset, mGlyphStyle); + int hashCode = Objects.hash(mXOffset, mYOffset); for (int i = 0; i < glyphCount(); ++i) { hashCode = Objects.hash(hashCode, - getGlyphId(i), getPositionX(i), getPositionY(i), getFont(i)); + getGlyphId(i), getGlyphX(i), getGlyphY(i), getFont(i)); } return hashCode; } @@ -263,7 +251,7 @@ public final class PositionedGlyphs { sb.append(", "); } sb.append("[ ID = " + getGlyphId(i) + "," - + " pos = (" + getPositionX(i) + "," + getPositionY(i) + ")" + + " pos = (" + getGlyphX(i) + "," + getGlyphY(i) + ")" + " font = " + getFont(i) + " ]"); } sb.append("]"); @@ -271,7 +259,6 @@ public final class PositionedGlyphs { + "glyphs = " + sb.toString() + ", mXOffset=" + mXOffset + ", mYOffset=" + mYOffset - + ", mGlyphStyle=" + mGlyphStyle + '}'; } } diff --git a/graphics/java/android/graphics/text/TextShaper.java b/graphics/java/android/graphics/text/TextRunShaper.java index f40ed8f8f653..b73436e36ae0 100644 --- a/graphics/java/android/graphics/text/TextShaper.java +++ b/graphics/java/android/graphics/text/TextRunShaper.java @@ -19,6 +19,7 @@ package android.graphics.text; import android.annotation.NonNull; import android.graphics.Paint; import android.text.TextDirectionHeuristic; +import android.text.TextPaint; import android.text.TextUtils; import com.android.internal.util.Preconditions; @@ -31,15 +32,18 @@ import dalvik.annotation.optimization.FastNative; * Text shaping is a preprocess for drawing text into canvas with glyphs. The glyph is a most * primitive unit of the text drawing, consist of glyph identifier in the font file and its position * and style. You can draw the shape result to Canvas by calling Canvas#drawGlyphs. - * - * @see TextShaper#shapeTextRun(CharSequence, int, int, int, int, float, float, boolean, Paint) - * @see TextShaper#shapeTextRun(char[], int, int, int, int, float, float, boolean, Paint) - * @see android.text.StyledTextShaper#shapeText(CharSequence, int, int, TextDirectionHeuristic, - * TextPaint) + * For most of the use cases, {@link android.text.TextShaper} will provide text shaping + * functionalities needed. {@link TextRunShaper} is a lower level API that is used by + * {@link android.text.TextShaper}. + * + * @see TextRunShaper#shapeTextRun(CharSequence, int, int, int, int, float, float, boolean, Paint) + * @see TextRunShaper#shapeTextRun(char[], int, int, int, int, float, float, boolean, Paint) + * @see android.text.TextShaper#shapeText(CharSequence, int, int, TextDirectionHeuristic, TextPaint, + * TextShaper.GlyphsConsumer) */ -public class TextShaper { - private TextShaper() {} // Do not instantiate +public class TextRunShaper { + private TextRunShaper() {} // Do not instantiate /** * Shape non-styled text. diff --git a/keystore/java/android/security/keystore/AttestationUtils.java b/keystore/java/android/security/keystore/AttestationUtils.java index c8d4be319bab..f48da74eb397 100644 --- a/keystore/java/android/security/keystore/AttestationUtils.java +++ b/keystore/java/android/security/keystore/AttestationUtils.java @@ -20,7 +20,6 @@ import android.Manifest; import android.annotation.NonNull; import android.annotation.RequiresPermission; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.content.Context; import android.os.Build; import android.security.KeyStore; @@ -44,7 +43,6 @@ import java.util.Set; * @hide */ @SystemApi -@TestApi public abstract class AttestationUtils { private AttestationUtils() { } diff --git a/keystore/java/android/security/keystore/DeviceIdAttestationException.java b/keystore/java/android/security/keystore/DeviceIdAttestationException.java index 8ba0317845d0..4f9f9e633a98 100644 --- a/keystore/java/android/security/keystore/DeviceIdAttestationException.java +++ b/keystore/java/android/security/keystore/DeviceIdAttestationException.java @@ -18,7 +18,6 @@ package android.security.keystore; import android.annotation.Nullable; import android.annotation.SystemApi; -import android.annotation.TestApi; /** * Thrown when {@link AttestationUtils} is unable to attest the given device ids. @@ -26,7 +25,6 @@ import android.annotation.TestApi; * @hide */ @SystemApi -@TestApi public class DeviceIdAttestationException extends Exception { /** * Constructs a new {@code DeviceIdAttestationException} with the current stack trace and the diff --git a/libs/WindowManager/Shell/res/raw/wm_shell_protolog.json b/libs/WindowManager/Shell/res/raw/wm_shell_protolog.json index bcef154beeb3..02bf38504725 100644 --- a/libs/WindowManager/Shell/res/raw/wm_shell_protolog.json +++ b/libs/WindowManager/Shell/res/raw/wm_shell_protolog.json @@ -1,8 +1,8 @@ { "version": "1.0.0", "messages": { - "-1823823103": { - "message": "Add listener for types=%s listener=%s", + "-1683614271": { + "message": "Existing task: id=%d component=%s", "level": "VERBOSE", "group": "WM_SHELL_TASK_ORG", "at": "com\/android\/wm\/shell\/ShellTaskOrganizer.java" @@ -31,6 +31,12 @@ "group": "WM_SHELL_TASK_ORG", "at": "com\/android\/wm\/shell\/ShellTaskOrganizer.java" }, + "-1312360667": { + "message": "createRootTask() displayId=%d winMode=%d listener=%s", + "level": "VERBOSE", + "group": "WM_SHELL_TASK_ORG", + "at": "com\/android\/wm\/shell\/ShellTaskOrganizer.java" + }, "-880817403": { "message": "Task vanished taskId=%d", "level": "VERBOSE", @@ -55,17 +61,35 @@ "group": "WM_SHELL_TASK_ORG", "at": "com\/android\/wm\/shell\/ShellTaskOrganizer.java" }, + "481673835": { + "message": "addListenerForTaskId taskId=%s", + "level": "VERBOSE", + "group": "WM_SHELL_TASK_ORG", + "at": "com\/android\/wm\/shell\/ShellTaskOrganizer.java" + }, "564235578": { "message": "Fullscreen Task Vanished: #%d", "level": "VERBOSE", "group": "WM_SHELL_TASK_ORG", "at": "com\/android\/wm\/shell\/FullscreenTaskListener.java" }, + "580605218": { + "message": "Registering organizer", + "level": "VERBOSE", + "group": "WM_SHELL_TASK_ORG", + "at": "com\/android\/wm\/shell\/ShellTaskOrganizer.java" + }, "980952660": { "message": "Task root back pressed taskId=%d", "level": "VERBOSE", "group": "WM_SHELL_TASK_ORG", "at": "com\/android\/wm\/shell\/ShellTaskOrganizer.java" + }, + "1990759023": { + "message": "addListenerForType types=%s listener=%s", + "level": "VERBOSE", + "group": "WM_SHELL_TASK_ORG", + "at": "com\/android\/wm\/shell\/ShellTaskOrganizer.java" } }, "groups": { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/FullscreenTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/FullscreenTaskListener.java index 9d6271bca426..4cb5fd139259 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/FullscreenTaskListener.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/FullscreenTaskListener.java @@ -16,17 +16,24 @@ package com.android.wm.shell; +import static com.android.wm.shell.ShellTaskOrganizer.TASK_LISTENER_TYPE_FULLSCREEN; +import static com.android.wm.shell.ShellTaskOrganizer.taskListenerTypeToString; + import android.app.ActivityManager; import android.util.ArraySet; import android.util.Slog; import android.view.SurfaceControl; +import androidx.annotation.NonNull; + import com.android.internal.protolog.common.ProtoLog; import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.protolog.ShellProtoLogGroup; +import java.io.PrintWriter; + class FullscreenTaskListener implements ShellTaskOrganizer.TaskListener { - private static final String TAG = "FullscreenTaskOrg"; + private static final String TAG = "FullscreenTaskListener"; private final SyncTransactionQueue mSyncQueue; @@ -74,6 +81,15 @@ class FullscreenTaskListener implements ShellTaskOrganizer.TaskListener { } @Override - public void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) { + public void dump(@NonNull PrintWriter pw, String prefix) { + final String innerPrefix = prefix + " "; + final String childPrefix = innerPrefix + " "; + pw.println(prefix + this); + pw.println(innerPrefix + mTasks.size() + " Tasks"); + } + + @Override + public String toString() { + return TAG + ":" + taskListenerTypeToString(TASK_LISTENER_TYPE_FULLSCREEN); } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java index 7ce65fd63334..8bd7193843f7 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java @@ -24,16 +24,22 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMAR import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; +import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_TASK_ORG; + import android.annotation.IntDef; import android.app.ActivityManager.RunningTaskInfo; import android.app.WindowConfiguration.WindowingMode; +import android.os.IBinder; +import android.util.ArrayMap; import android.util.Log; -import android.util.Pair; import android.util.SparseArray; import android.view.SurfaceControl; import android.window.ITaskOrganizerController; +import android.window.TaskAppearedInfo; import android.window.TaskOrganizer; +import androidx.annotation.NonNull; + import com.android.internal.annotations.VisibleForTesting; import com.android.internal.protolog.common.ProtoLog; import com.android.wm.shell.common.ShellExecutor; @@ -41,7 +47,10 @@ import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.common.TransactionPool; import com.android.wm.shell.protolog.ShellProtoLogGroup; +import java.io.PrintWriter; +import java.util.ArrayList; import java.util.Arrays; +import java.util.List; /** * Unified task organizer for all components in the shell. @@ -76,13 +85,22 @@ public class ShellTaskOrganizer extends TaskOrganizer { default void onTaskInfoChanged(RunningTaskInfo taskInfo) {} default void onTaskVanished(RunningTaskInfo taskInfo) {} default void onBackPressedOnTaskRoot(RunningTaskInfo taskInfo) {} + default void dump(@NonNull PrintWriter pw, String prefix) {}; } - private final SparseArray<TaskListener> mTaskListenersByType = new SparseArray<>(); + /** + * Keys map from either a task id or {@link TaskListenerType}. + * @see #addListenerForTaskId + * @see #addListenerForType + */ + private final SparseArray<TaskListener> mTaskListeners = new SparseArray<>(); // Keeps track of all the tasks reported to this organizer (changes in windowing mode will // require us to report to both old and new listeners) - private final SparseArray<Pair<RunningTaskInfo, SurfaceControl>> mTasks = new SparseArray<>(); + private final SparseArray<TaskAppearedInfo> mTasks = new SparseArray<>(); + + /** @see #setPendingLaunchCookieListener */ + private final ArrayMap<IBinder, TaskListener> mLaunchCookieToListener = new ArrayMap<>(); // TODO(shell-transitions): move to a more "global" Shell location as this isn't only for Tasks private final Transitions mTransitions; @@ -97,31 +115,75 @@ public class ShellTaskOrganizer extends TaskOrganizer { SyncTransactionQueue syncQueue, TransactionPool transactionPool, ShellExecutor mainExecutor, ShellExecutor animExecutor) { super(taskOrganizerController); - addListener(new FullscreenTaskListener(syncQueue), TASK_LISTENER_TYPE_FULLSCREEN); + addListenerForType(new FullscreenTaskListener(syncQueue), TASK_LISTENER_TYPE_FULLSCREEN); mTransitions = new Transitions(this, transactionPool, mainExecutor, animExecutor); if (Transitions.ENABLE_SHELL_TRANSITIONS) registerTransitionPlayer(mTransitions); } + @Override + public List<TaskAppearedInfo> registerOrganizer() { + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Registering organizer"); + final List<TaskAppearedInfo> taskInfos = super.registerOrganizer(); + for (int i = 0; i < taskInfos.size(); i++) { + final TaskAppearedInfo info = taskInfos.get(i); + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Existing task: id=%d component=%s", + info.getTaskInfo().taskId, info.getTaskInfo().baseIntent); + onTaskAppeared(info); + } + return taskInfos; + } + + public TaskAppearedInfo createRootTask( + int displayId, int windowingMode, TaskListener listener) { + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, + "createRootTask() displayId=%d winMode=%d listener=%s", + displayId, windowingMode, listener.toString()); + final TaskAppearedInfo info = super.createRootTask(displayId, windowingMode); + + // Add the listener and send the task appeared signal + mTaskListeners.put(info.getTaskInfo().taskId, listener); + onTaskAppeared(info); + return info; + } + + /** + * Adds a listener for a specific task id. + */ + public void addListenerForTaskId(TaskListener listener, int taskId) { + ProtoLog.v(WM_SHELL_TASK_ORG, "addListenerForTaskId taskId=%s", taskId); + if (mTaskListeners.get(taskId) != null) { + throw new IllegalArgumentException("Listener for taskId=" + taskId + " already exists"); + } + + final TaskAppearedInfo info = mTasks.get(taskId); + if (info == null) { + throw new IllegalArgumentException("addListenerForTaskId unknown taskId=" + taskId); + } + + final TaskListener oldListener = getTaskListener(info.getTaskInfo()); + mTaskListeners.put(taskId, listener); + updateTaskListenerIfNeeded(info.getTaskInfo(), info.getLeash(), oldListener, listener); + } + /** * Adds a listener for tasks with given types. */ - public void addListener(TaskListener listener, @TaskListenerType int... taskListenerTypes) { - ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Add listener for types=%s listener=%s", - Arrays.toString(taskListenerTypes), listener); - for (int listenerType : taskListenerTypes) { - if (mTaskListenersByType.get(listenerType) != null) { + public void addListenerForType(TaskListener listener, @TaskListenerType int... listenerTypes) { + ProtoLog.v(WM_SHELL_TASK_ORG, "addListenerForType types=%s listener=%s", + Arrays.toString(listenerTypes), listener); + for (int listenerType : listenerTypes) { + if (mTaskListeners.get(listenerType) != null) { throw new IllegalArgumentException("Listener for listenerType=" + listenerType + " already exists"); } - mTaskListenersByType.put(listenerType, listener); + mTaskListeners.put(listenerType, listener); // Notify the listener of all existing tasks with the given type. - for (int i = mTasks.size() - 1; i >= 0; i--) { - Pair<RunningTaskInfo, SurfaceControl> data = mTasks.valueAt(i); - final @TaskListenerType int taskListenerType = getTaskListenerType(data.first); - if (taskListenerType == listenerType) { - listener.onTaskAppeared(data.first, data.second); - } + for (int i = mTasks.size() - 1; i >= 0; --i) { + final TaskAppearedInfo data = mTasks.valueAt(i); + final TaskListener taskListener = getTaskListener(data.getTaskInfo()); + if (taskListener != listener) continue; + listener.onTaskAppeared(data.getTaskInfo(), data.getLeash()); } } } @@ -130,59 +192,75 @@ public class ShellTaskOrganizer extends TaskOrganizer { * Removes a registered listener. */ public void removeListener(TaskListener listener) { - ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Remove listener=%s", listener); - final int index = mTaskListenersByType.indexOfValue(listener); + ProtoLog.v(WM_SHELL_TASK_ORG, "Remove listener=%s", listener); + final int index = mTaskListeners.indexOfValue(listener); if (index == -1) { Log.w(TAG, "No registered listener found"); return; } - mTaskListenersByType.removeAt(index); + + // Collect tasks associated with the listener we are about to remove. + final ArrayList<TaskAppearedInfo> tasks = new ArrayList<>(); + for (int i = mTasks.size() - 1; i >= 0; --i) { + final TaskAppearedInfo data = mTasks.valueAt(i); + final TaskListener taskListener = getTaskListener(data.getTaskInfo()); + if (taskListener != listener) continue; + tasks.add(data); + } + + // Remove listener + mTaskListeners.removeAt(index); + + // Associate tasks with new listeners if needed. + for (int i = tasks.size() - 1; i >= 0; --i) { + final TaskAppearedInfo data = tasks.get(i); + updateTaskListenerIfNeeded(data.getTaskInfo(), data.getLeash(), + null /* oldListener already removed*/, getTaskListener(data.getTaskInfo())); + } + } + + /** + * Associated a listener to a pending launch cookie so we can route the task later once it + * appears. + */ + public void setPendingLaunchCookieListener(IBinder cookie, TaskListener listener) { + mLaunchCookieToListener.put(cookie, listener); } @Override public void onTaskAppeared(RunningTaskInfo taskInfo, SurfaceControl leash) { - ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Task appeared taskId=%d", - taskInfo.taskId); - mTasks.put(taskInfo.taskId, new Pair<>(taskInfo, leash)); - final TaskListener listener = mTaskListenersByType.get(getTaskListenerType(taskInfo)); + onTaskAppeared(new TaskAppearedInfo(taskInfo, leash)); + } + + private void onTaskAppeared(TaskAppearedInfo info) { + final int taskId = info.getTaskInfo().taskId; + ProtoLog.v(WM_SHELL_TASK_ORG, "Task appeared taskId=%d", taskId); + mTasks.put(taskId, info); + final TaskListener listener = + getTaskListener(info.getTaskInfo(), true /*removeLaunchCookieIfNeeded*/); if (listener != null) { - listener.onTaskAppeared(taskInfo, leash); + listener.onTaskAppeared(info.getTaskInfo(), info.getLeash()); } } @Override public void onTaskInfoChanged(RunningTaskInfo taskInfo) { - ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Task info changed taskId=%d", - taskInfo.taskId); - final Pair<RunningTaskInfo, SurfaceControl> data = mTasks.get(taskInfo.taskId); - final @TaskListenerType int listenerType = getTaskListenerType(taskInfo); - final @TaskListenerType int prevListenerType = getTaskListenerType(data.first); - mTasks.put(taskInfo.taskId, new Pair<>(taskInfo, data.second)); - if (prevListenerType != listenerType) { - // TODO: We currently send vanished/appeared as the task moves between types, but - // we should consider adding a different mode-changed callback - TaskListener listener = mTaskListenersByType.get(prevListenerType); - if (listener != null) { - listener.onTaskVanished(taskInfo); - } - listener = mTaskListenersByType.get(listenerType); - if (listener != null) { - SurfaceControl leash = data.second; - listener.onTaskAppeared(taskInfo, leash); - } - } else { - final TaskListener listener = mTaskListenersByType.get(listenerType); - if (listener != null) { - listener.onTaskInfoChanged(taskInfo); - } + ProtoLog.v(WM_SHELL_TASK_ORG, "Task info changed taskId=%d", taskInfo.taskId); + final TaskAppearedInfo data = mTasks.get(taskInfo.taskId); + final TaskListener oldListener = getTaskListener(data.getTaskInfo()); + final TaskListener newListener = getTaskListener(taskInfo); + mTasks.put(taskInfo.taskId, new TaskAppearedInfo(taskInfo, data.getLeash())); + final boolean updated = updateTaskListenerIfNeeded( + taskInfo, data.getLeash(), oldListener, newListener); + if (!updated && newListener != null) { + newListener.onTaskInfoChanged(taskInfo); } } @Override public void onBackPressedOnTaskRoot(RunningTaskInfo taskInfo) { - ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Task root back pressed taskId=%d", - taskInfo.taskId); - final TaskListener listener = mTaskListenersByType.get(getTaskListenerType(taskInfo)); + ProtoLog.v(WM_SHELL_TASK_ORG, "Task root back pressed taskId=%d", taskInfo.taskId); + final TaskListener listener = getTaskListener(taskInfo); if (listener != null) { listener.onBackPressedOnTaskRoot(taskInfo); } @@ -190,22 +268,72 @@ public class ShellTaskOrganizer extends TaskOrganizer { @Override public void onTaskVanished(RunningTaskInfo taskInfo) { - ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TASK_ORG, "Task vanished taskId=%d", - taskInfo.taskId); - final @TaskListenerType int prevListenerType = - getTaskListenerType(mTasks.get(taskInfo.taskId).first); - mTasks.remove(taskInfo.taskId); - final TaskListener listener = mTaskListenersByType.get(prevListenerType); + ProtoLog.v(WM_SHELL_TASK_ORG, "Task vanished taskId=%d", taskInfo.taskId); + final int taskId = taskInfo.taskId; + final TaskListener listener = getTaskListener(mTasks.get(taskId).getTaskInfo()); + mTasks.remove(taskId); if (listener != null) { listener.onTaskVanished(taskInfo); } } - @TaskListenerType - private static int getTaskListenerType(RunningTaskInfo runningTaskInfo) { - // Right now it's N:1 mapping but in the future different task listerners - // may be triggered by one windowing mode depending on task parameters. - switch (getWindowingMode(runningTaskInfo)) { + private boolean updateTaskListenerIfNeeded(RunningTaskInfo taskInfo, SurfaceControl leash, + TaskListener oldListener, TaskListener newListener) { + if (oldListener == newListener) return false; + // TODO: We currently send vanished/appeared as the task moves between types, but + // we should consider adding a different mode-changed callback + if (oldListener != null) { + oldListener.onTaskVanished(taskInfo); + } + if (newListener != null) { + newListener.onTaskAppeared(taskInfo, leash); + } + return true; + } + + private TaskListener getTaskListener(RunningTaskInfo runningTaskInfo) { + return getTaskListener(runningTaskInfo, false /*removeLaunchCookieIfNeeded*/); + } + + private TaskListener getTaskListener(RunningTaskInfo runningTaskInfo, + boolean removeLaunchCookieIfNeeded) { + + final int taskId = runningTaskInfo.taskId; + TaskListener listener; + + // First priority goes to listener that might be pending for this task. + final ArrayList<IBinder> launchCookies = runningTaskInfo.launchCookies; + for (int i = launchCookies.size() - 1; i >= 0; --i) { + final IBinder cookie = launchCookies.get(i); + listener = mLaunchCookieToListener.get(cookie); + if (listener == null) continue; + + if (removeLaunchCookieIfNeeded) { + // Remove the cookie and add the listener. + mLaunchCookieToListener.remove(cookie); + mTaskListeners.put(taskId, listener); + } + return listener; + } + + // Next priority goes to taskId specific listeners. + listener = mTaskListeners.get(taskId); + if (listener != null) return listener; + + // Next we try type specific listeners. + final int windowingMode = getWindowingMode(runningTaskInfo); + final int taskListenerType = windowingModeToTaskListenerType(windowingMode); + return mTaskListeners.get(taskListenerType); + } + + @WindowingMode + private static int getWindowingMode(RunningTaskInfo taskInfo) { + return taskInfo.configuration.windowConfiguration.getWindowingMode(); + } + + private static @TaskListenerType int windowingModeToTaskListenerType( + @WindowingMode int windowingMode) { + switch (windowingMode) { case WINDOWING_MODE_FULLSCREEN: return TASK_LISTENER_TYPE_FULLSCREEN; case WINDOWING_MODE_MULTI_WINDOW: @@ -222,8 +350,50 @@ public class ShellTaskOrganizer extends TaskOrganizer { } } - @WindowingMode - private static int getWindowingMode(RunningTaskInfo taskInfo) { - return taskInfo.configuration.windowConfiguration.getWindowingMode(); + public static String taskListenerTypeToString(@TaskListenerType int type) { + switch (type) { + case TASK_LISTENER_TYPE_FULLSCREEN: + return "TASK_LISTENER_TYPE_FULLSCREEN"; + case TASK_LISTENER_TYPE_MULTI_WINDOW: + return "TASK_LISTENER_TYPE_MULTI_WINDOW"; + case TASK_LISTENER_TYPE_SPLIT_SCREEN: + return "TASK_LISTENER_TYPE_SPLIT_SCREEN"; + case TASK_LISTENER_TYPE_PIP: + return "TASK_LISTENER_TYPE_PIP"; + case TASK_LISTENER_TYPE_UNDEFINED: + return "TASK_LISTENER_TYPE_UNDEFINED"; + default: + return "taskId#" + type; + } + } + + public void dump(@NonNull PrintWriter pw, String prefix) { + final String innerPrefix = prefix + " "; + final String childPrefix = innerPrefix + " "; + pw.println(prefix + TAG); + pw.println(innerPrefix + mTaskListeners.size() + " Listeners"); + for (int i = mTaskListeners.size() - 1; i >= 0; --i) { + final int key = mTaskListeners.keyAt(i); + final TaskListener listener = mTaskListeners.valueAt(i); + pw.println(innerPrefix + "#" + i + " " + taskListenerTypeToString(key)); + listener.dump(pw, childPrefix); + } + + pw.println(); + pw.println(innerPrefix + mTasks.size() + " Tasks"); + for (int i = mTasks.size() - 1; i >= 0; --i) { + final int key = mTasks.keyAt(i); + final TaskAppearedInfo info = mTasks.valueAt(i); + final TaskListener listener = getTaskListener(info.getTaskInfo()); + pw.println(innerPrefix + "#" + i + " task=" + key + " listener=" + listener); + } + + pw.println(); + pw.println(innerPrefix + mLaunchCookieToListener.size() + " Launch Cookies"); + for (int i = mLaunchCookieToListener.size() - 1; i >= 0; --i) { + final IBinder key = mLaunchCookieToListener.keyAt(i); + final TaskListener listener = mLaunchCookieToListener.valueAt(i); + pw.println(innerPrefix + "#" + i + " cookie=" + key + " listener=" + listener); + } } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java index d810fb8257a9..ea18a19c2ee5 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java @@ -69,11 +69,6 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged private final SparseArray<PerDisplay> mImePerDisplay = new SparseArray<>(); private final ArrayList<ImePositionProcessor> mPositionProcessors = new ArrayList<>(); - @Deprecated - public DisplayImeController(IWindowManager wmService, DisplayController displayController, - Handler mainHandler, TransactionPool transactionPool) { - this(wmService, displayController, mainHandler::post, transactionPool); - } public DisplayImeController(IWindowManager wmService, DisplayController displayController, Executor mainExecutor, TransactionPool transactionPool) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java index 84b98f9e8047..eaf5d7960aa9 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java @@ -32,7 +32,7 @@ import android.util.Slog; import android.util.SparseArray; import android.view.Display; import android.view.DragEvent; -import android.view.IScrollCaptureController; +import android.view.IScrollCaptureCallbacks; import android.view.IWindow; import android.view.IWindowManager; import android.view.IWindowSession; @@ -276,12 +276,11 @@ public class SystemWindows { long frameNumber, ClientWindowFrames outFrames, MergedConfiguration mergedConfiguration, SurfaceControl outSurfaceControl, InsetsState outInsetsState, - InsetsSourceControl[] outActiveControls, Point outSurfaceSize, - SurfaceControl outBLASTSurfaceControl) { + InsetsSourceControl[] outActiveControls, Point outSurfaceSize) { int res = super.relayout(window, attrs, requestedWidth, requestedHeight, viewVisibility, flags, frameNumber, outFrames, mergedConfiguration, outSurfaceControl, outInsetsState, - outActiveControls, outSurfaceSize, outBLASTSurfaceControl); + outActiveControls, outSurfaceSize); if (res != 0) { return res; } @@ -374,9 +373,9 @@ public class SystemWindows { public void dispatchPointerCaptureChanged(boolean hasCapture) {} @Override - public void requestScrollCapture(IScrollCaptureController controller) { + public void requestScrollCapture(IScrollCaptureCallbacks callbacks) { try { - controller.onClientUnavailable(); + callbacks.onUnavailable(); } catch (RemoteException ex) { // ignore } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java index 488f9092b7da..3ded4091ec11 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java @@ -98,7 +98,7 @@ public interface Pip { /** * Hides the PIP menu. */ - void hidePipMenu(Runnable onStartCallback, Runnable onEndCallback); + default void hidePipMenu(Runnable onStartCallback, Runnable onEndCallback) {} /** * Returns {@code true} if PIP is shown. @@ -226,7 +226,7 @@ public interface Pip { /** * Called when showing Pip menu. */ - void showPictureInPictureMenu(); + default void showPictureInPictureMenu() {} /** * Suspends resizing operation on the Pip until {@link #resumePipResizing} is called. diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsHandler.java index de3261baf9b7..6d6cc204538a 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsHandler.java @@ -22,9 +22,9 @@ import static android.util.TypedValue.COMPLEX_UNIT_DIP; import static android.view.Surface.ROTATION_0; import static android.view.Surface.ROTATION_180; +import android.annotation.NonNull; import android.app.ActivityTaskManager; import android.app.ActivityTaskManager.RootTaskInfo; -import android.content.ComponentName; import android.content.Context; import android.content.res.Resources; import android.graphics.Point; @@ -52,14 +52,11 @@ public class PipBoundsHandler { private static final String TAG = PipBoundsHandler.class.getSimpleName(); private static final float INVALID_SNAP_FRACTION = -1f; + private final @NonNull PipBoundsState mPipBoundsState; private final PipSnapAlgorithm mSnapAlgorithm; private final DisplayInfo mDisplayInfo = new DisplayInfo(); private DisplayLayout mDisplayLayout; - private ComponentName mLastPipComponentName; - private float mReentrySnapFraction = INVALID_SNAP_FRACTION; - private Size mReentrySize; - private float mDefaultAspectRatio; private float mMinAspectRatio; private float mMaxAspectRatio; @@ -75,7 +72,8 @@ public class PipBoundsHandler { private boolean mIsShelfShowing; private int mShelfHeight; - public PipBoundsHandler(Context context) { + public PipBoundsHandler(Context context, @NonNull PipBoundsState pipBoundsState) { + mPipBoundsState = pipBoundsState; mSnapAlgorithm = new PipSnapAlgorithm(context); mDisplayLayout = new DisplayLayout(); reloadResources(context); @@ -121,6 +119,13 @@ public class PipBoundsHandler { } /** + * Get the current saved display info. + */ + public DisplayInfo getDisplayInfo() { + return mDisplayInfo; + } + + /** * Update the Min edge size for {@link PipSnapAlgorithm} to calculate corresponding bounds * @param minEdgeSize */ @@ -175,40 +180,6 @@ public class PipBoundsHandler { } /** - * Responds to IPinnedStackListener on saving reentry snap fraction and size - * for a given {@link ComponentName}. - */ - public void onSaveReentryBounds(ComponentName componentName, Rect bounds) { - mReentrySnapFraction = getSnapFraction(bounds); - mReentrySize = new Size(bounds.width(), bounds.height()); - mLastPipComponentName = componentName; - } - - /** - * Responds to IPinnedStackListener on resetting reentry snap fraction and size - * for a given {@link ComponentName}. - */ - public void onResetReentryBounds(ComponentName componentName) { - if (componentName.equals(mLastPipComponentName)) { - onResetReentryBoundsUnchecked(); - } - } - - private void onResetReentryBoundsUnchecked() { - mReentrySnapFraction = INVALID_SNAP_FRACTION; - mReentrySize = null; - mLastPipComponentName = null; - } - - /** - * Returns ture if there's a valid snap fraction. This is used with {@link EXTRA_IS_FIRST_ENTRY} - * to see if this is the first time user has entered PIP for the component. - */ - public boolean hasSaveReentryBounds() { - return mReentrySnapFraction != INVALID_SNAP_FRACTION; - } - - /** * The {@link PipSnapAlgorithm} is couple on display bounds * @return {@link PipSnapAlgorithm}. */ @@ -250,37 +221,43 @@ public class PipBoundsHandler { } /** - * See {@link #getDestinationBounds(ComponentName, float, Rect, Size, boolean)} + * See {@link #getDestinationBounds(float, Rect, Size, boolean)} */ - public Rect getDestinationBounds(ComponentName componentName, float aspectRatio, Rect bounds, - Size minimalSize) { - return getDestinationBounds(componentName, aspectRatio, bounds, minimalSize, + public Rect getDestinationBounds(float aspectRatio, Rect bounds, Size minimalSize) { + return getDestinationBounds(aspectRatio, bounds, minimalSize, false /* useCurrentMinEdgeSize */); } /** * @return {@link Rect} of the destination PiP window bounds. */ - public Rect getDestinationBounds(ComponentName componentName, float aspectRatio, Rect bounds, + public Rect getDestinationBounds(float aspectRatio, Rect bounds, Size minimalSize, boolean useCurrentMinEdgeSize) { - if (!componentName.equals(mLastPipComponentName)) { - onResetReentryBoundsUnchecked(); - mLastPipComponentName = componentName; - } + boolean isReentryBounds = false; final Rect destinationBounds; if (bounds == null) { - final Rect defaultBounds = getDefaultBounds(mReentrySnapFraction, mReentrySize); - destinationBounds = new Rect(defaultBounds); - if (mReentrySnapFraction == INVALID_SNAP_FRACTION && mReentrySize == null) { + // Calculating initial entry bounds + final PipBoundsState.PipReentryState state = mPipBoundsState.getReentryState(); + + final Rect defaultBounds; + if (state != null) { + // Restore to reentry bounds. + defaultBounds = getDefaultBounds(state.getSnapFraction(), state.getSize()); + isReentryBounds = true; + } else { + // Get actual default bounds. + defaultBounds = getDefaultBounds(INVALID_SNAP_FRACTION, null /* size */); mOverrideMinimalSize = minimalSize; } + + destinationBounds = new Rect(defaultBounds); } else { + // Just adjusting bounds (e.g. on aspect ratio changed). destinationBounds = new Rect(bounds); } if (isValidPictureInPictureAspectRatio(aspectRatio)) { - boolean useCurrentSize = bounds == null && mReentrySize != null; transformBoundsToAspectRatio(destinationBounds, aspectRatio, useCurrentMinEdgeSize, - useCurrentSize); + isReentryBounds); } mAspectRatio = aspectRatio; return destinationBounds; @@ -533,9 +510,6 @@ public class PipBoundsHandler { public void dump(PrintWriter pw, String prefix) { final String innerPrefix = prefix + " "; pw.println(prefix + TAG); - pw.println(innerPrefix + "mLastPipComponentName=" + mLastPipComponentName); - pw.println(innerPrefix + "mReentrySnapFraction=" + mReentrySnapFraction); - pw.println(innerPrefix + "mReentrySize=" + mReentrySize); pw.println(innerPrefix + "mDisplayInfo=" + mDisplayInfo); pw.println(innerPrefix + "mDefaultAspectRatio=" + mDefaultAspectRatio); pw.println(innerPrefix + "mMinAspectRatio=" + mMinAspectRatio); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java index 10e5c3d33def..2625f16fac46 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java @@ -17,9 +17,15 @@ package com.android.wm.shell.pip; import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.ComponentName; import android.graphics.Rect; +import android.util.Size; + +import com.android.internal.annotations.VisibleForTesting; import java.io.PrintWriter; +import java.util.Objects; /** * Singleton source of truth for the current state of PIP bounds. @@ -28,6 +34,8 @@ public final class PipBoundsState { private static final String TAG = PipBoundsState.class.getSimpleName(); private final @NonNull Rect mBounds = new Rect(); + private PipReentryState mPipReentryState; + private ComponentName mLastPipComponentName; void setBounds(@NonNull Rect bounds) { mBounds.set(bounds); @@ -39,11 +47,83 @@ public final class PipBoundsState { } /** + * Save the reentry state to restore to when re-entering PIP mode. + * + * TODO(b/169373982): consider refactoring this so that this class alone can use mBounds and + * calculate the snap fraction to save for re-entry. + */ + public void saveReentryState(@NonNull Rect bounds, float fraction) { + mPipReentryState = new PipReentryState(new Size(bounds.width(), bounds.height()), fraction); + } + + /** + * Returns the saved reentry state. + */ + @Nullable + public PipReentryState getReentryState() { + return mPipReentryState; + } + + /** + * Set the last {@link ComponentName} to enter PIP mode. + */ + public void setLastPipComponentName(ComponentName lastPipComponentName) { + final boolean changed = !Objects.equals(mLastPipComponentName, lastPipComponentName); + mLastPipComponentName = lastPipComponentName; + if (changed) { + clearReentryState(); + } + } + + public ComponentName getLastPipComponentName() { + return mLastPipComponentName; + } + + @VisibleForTesting + void clearReentryState() { + mPipReentryState = null; + } + + static final class PipReentryState { + private static final String TAG = PipReentryState.class.getSimpleName(); + + private final @NonNull Size mSize; + private final float mSnapFraction; + + PipReentryState(@NonNull Size size, float snapFraction) { + mSize = size; + mSnapFraction = snapFraction; + } + + @NonNull + Size getSize() { + return mSize; + } + + float getSnapFraction() { + return mSnapFraction; + } + + void dump(PrintWriter pw, String prefix) { + final String innerPrefix = prefix + " "; + pw.println(prefix + TAG); + pw.println(innerPrefix + "mSize=" + mSize); + pw.println(innerPrefix + "mSnapFraction=" + mSnapFraction); + } + } + + /** * Dumps internal state. */ public void dump(PrintWriter pw, String prefix) { final String innerPrefix = prefix + " "; pw.println(prefix + TAG); pw.println(innerPrefix + "mBounds=" + mBounds); + pw.println(innerPrefix + "mLastPipComponentName=" + mLastPipComponentName); + if (mPipReentryState == null) { + pw.println(innerPrefix + "mPipReentryState=null"); + } else { + mPipReentryState.dump(pw, innerPrefix); + } } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java index 3485c7ae8ecb..cd5d35bf0c4c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java @@ -22,6 +22,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECOND import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static com.android.wm.shell.ShellTaskOrganizer.TASK_LISTENER_TYPE_PIP; +import static com.android.wm.shell.ShellTaskOrganizer.taskListenerTypeToString; import static com.android.wm.shell.pip.PipAnimationController.ANIM_TYPE_ALPHA; import static com.android.wm.shell.pip.PipAnimationController.ANIM_TYPE_BOUNDS; import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_LEAVE_PIP; @@ -78,6 +79,7 @@ import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.function.Consumer; +import java.util.function.IntConsumer; /** * Manages PiP tasks such as resize and offset. @@ -247,6 +249,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, private PipSurfaceTransactionHelper.SurfaceControlTransactionFactory mSurfaceControlTransactionFactory; private PictureInPictureParams mPictureInPictureParams; + private IntConsumer mOnDisplayIdChangeCallback; /** * If set to {@code true}, the entering animation will be skipped and we will wait for @@ -281,13 +284,8 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, mSurfaceControlTransactionFactory = SurfaceControl.Transaction::new; mSplitScreenOptional = splitScreenOptional; mTaskOrganizer = shellTaskOrganizer; - - if (!PipUtils.hasSystemFeature(context)) { - Log.w(TAG, "Device not support PIP feature"); - } else { - mTaskOrganizer.addListener(this, TASK_LISTENER_TYPE_PIP); - displayController.addDisplayWindowListener(this); - } + mTaskOrganizer.addListenerForType(this, TASK_LISTENER_TYPE_PIP); + displayController.addDisplayWindowListener(this); } public Handler getUpdateHandler() { @@ -319,6 +317,13 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, } /** + * Registers a callback when a display change has been detected when we enter PiP. + */ + public void registerOnDisplayIdChangeCallback(IntConsumer onDisplayIdChangeCallback) { + mOnDisplayIdChangeCallback = onDisplayIdChangeCallback; + } + + /** * Sets the preferred animation type for one time. * This is typically used to set the animation type to * {@link PipAnimationController#ANIM_TYPE_ALPHA}. @@ -335,7 +340,8 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, PictureInPictureParams pictureInPictureParams) { mShouldIgnoreEnteringPipTransition = true; mState = State.ENTERING_PIP; - return mPipBoundsHandler.getDestinationBounds(componentName, + mPipBoundsState.setLastPipComponentName(componentName); + return mPipBoundsHandler.getDestinationBounds( getAspectRatioOrDefault(pictureInPictureParams), null /* bounds */, getMinimalSize(activityInfo)); } @@ -406,9 +412,13 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, @Override public void onTransactionReady(int id, SurfaceControl.Transaction t) { t.apply(); - scheduleAnimateResizePip(mPipBoundsState.getBounds(), - destinationBounds, getValidSourceHintRect(mTaskInfo, destinationBounds), - direction, animationDurationMs, null /* updateBoundsCallback */); + // Make sure to grab the latest source hint rect as it could have been updated + // right after applying the windowing mode change. + final Rect sourceHintRect = getValidSourceHintRect(mPictureInPictureParams, + destinationBounds); + scheduleAnimateResizePip(mPipBoundsState.getBounds(), destinationBounds, + sourceHintRect, direction, animationDurationMs, + null /* updateBoundsCallback */); mState = State.EXITING_PIP; } }); @@ -471,10 +481,18 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, mLeash = leash; mInitialState.put(mToken.asBinder(), new Configuration(mTaskInfo.configuration)); mPictureInPictureParams = mTaskInfo.pictureInPictureParams; + mPipBoundsState.setLastPipComponentName(mTaskInfo.topActivity); mPipUiEventLoggerLogger.setTaskInfo(mTaskInfo); mPipUiEventLoggerLogger.log(PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_ENTER); + // If the displayId of the task is different than what PipBoundsHandler has, then update + // it. This is possible if we entered PiP on an external display. + if (info.displayId != mPipBoundsHandler.getDisplayInfo().displayId + && mOnDisplayIdChangeCallback != null) { + mOnDisplayIdChangeCallback.accept(info.displayId); + } + if (mShouldIgnoreEnteringPipTransition) { // Animation has been finished together with Recents, directly apply the sync // transaction to PiP here. @@ -497,13 +515,14 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, } final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds( - mTaskInfo.topActivity, getAspectRatioOrDefault(mPictureInPictureParams), + getAspectRatioOrDefault(mPictureInPictureParams), null /* bounds */, getMinimalSize(mTaskInfo.topActivityInfo)); Objects.requireNonNull(destinationBounds, "Missing destination bounds"); final Rect currentBounds = mTaskInfo.configuration.windowConfiguration.getBounds(); if (mOneShotAnimationType == ANIM_TYPE_BOUNDS) { - final Rect sourceHintRect = getValidSourceHintRect(info, currentBounds); + final Rect sourceHintRect = getValidSourceHintRect(info.pictureInPictureParams, + currentBounds); scheduleAnimateResizePip(currentBounds, destinationBounds, sourceHintRect, TRANSITION_DIRECTION_TO_PIP, mEnterExitAnimationDuration, null /* updateBoundsCallback */); @@ -520,10 +539,10 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, * Returns the source hint rect if it is valid (if provided and is contained by the current * task bounds). */ - private Rect getValidSourceHintRect(ActivityManager.RunningTaskInfo info, Rect sourceBounds) { - final Rect sourceHintRect = info.pictureInPictureParams != null - && info.pictureInPictureParams.hasSourceBoundsHint() - ? info.pictureInPictureParams.getSourceRectHint() + private Rect getValidSourceHintRect(PictureInPictureParams params, Rect sourceBounds) { + final Rect sourceHintRect = params != null + && params.hasSourceBoundsHint() + ? params.getSourceRectHint() : null; if (sourceHintRect != null && sourceBounds.contains(sourceHintRect)) { return sourceHintRect; @@ -692,13 +711,14 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, @Override public void onTaskInfoChanged(ActivityManager.RunningTaskInfo info) { Objects.requireNonNull(mToken, "onTaskInfoChanged requires valid existing mToken"); + mPipBoundsState.setLastPipComponentName(info.topActivity); final PictureInPictureParams newParams = info.pictureInPictureParams; if (newParams == null || !applyPictureInPictureParams(newParams)) { Log.d(TAG, "Ignored onTaskInfoChanged with PiP param: " + newParams); return; } final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds( - info.topActivity, getAspectRatioOrDefault(newParams), + getAspectRatioOrDefault(newParams), mPipBoundsState.getBounds(), getMinimalSize(info.topActivityInfo), true /* userCurrentMinEdgeSize */); Objects.requireNonNull(destinationBounds, "Missing destination bounds"); @@ -715,7 +735,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, public void onFixedRotationFinished(int displayId) { if (mShouldDeferEnteringPip && mState.isInPip()) { final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds( - mTaskInfo.topActivity, getAspectRatioOrDefault(mPictureInPictureParams), + getAspectRatioOrDefault(mPictureInPictureParams), null /* bounds */, getMinimalSize(mTaskInfo.topActivityInfo)); // schedule a regular animation to ensure all the callbacks are still being sent enterPipWithAlphaAnimation(destinationBounds, 0 /* durationMs */); @@ -789,7 +809,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, } final Rect newDestinationBounds = mPipBoundsHandler.getDestinationBounds( - mTaskInfo.topActivity, getAspectRatioOrDefault(mPictureInPictureParams), + getAspectRatioOrDefault(mPictureInPictureParams), null /* bounds */, getMinimalSize(mTaskInfo.topActivityInfo)); if (newDestinationBounds.equals(currentDestinationBounds)) return; if (animator.getAnimationType() == ANIM_TYPE_BOUNDS) { @@ -1129,6 +1149,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, /** * Dumps internal states. */ + @Override public void dump(PrintWriter pw, String prefix) { final String innerPrefix = prefix + " "; pw.println(prefix + TAG); @@ -1146,6 +1167,11 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, } } + @Override + public String toString() { + return TAG + ":" + taskListenerTypeToString(TASK_LISTENER_TYPE_PIP); + } + /** * Callback interface for PiP transitions (both from and to PiP mode) */ diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java index d191c980375c..a08983740f9a 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java @@ -17,11 +17,10 @@ package com.android.wm.shell.pip.phone; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; +import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE; import static com.android.wm.shell.pip.PipAnimationController.isOutPipDirection; -import android.annotation.NonNull; -import android.annotation.Nullable; import android.app.ActivityManager; import android.app.PictureInPictureParams; import android.app.RemoteAction; @@ -34,11 +33,14 @@ import android.os.Handler; import android.os.RemoteException; import android.os.UserHandle; import android.os.UserManager; -import android.util.Log; +import android.util.Slog; import android.view.DisplayInfo; import android.view.IPinnedStackController; import android.window.WindowContainerTransaction; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + import com.android.wm.shell.WindowManagerShellWrapper; import com.android.wm.shell.common.DisplayChangeController; import com.android.wm.shell.common.DisplayController; @@ -171,7 +173,13 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac @Override public void onActivityHidden(ComponentName componentName) { - mHandler.post(() -> mPipBoundsHandler.onResetReentryBounds(componentName)); + mHandler.post(() -> { + if (componentName.equals(mPipBoundsState.getLastPipComponentName())) { + // The activity was removed, we don't want to restore to the reentry state + // saved for this component anymore. + mPipBoundsState.setLastPipComponentName(null); + } + }); } @Override @@ -196,7 +204,7 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac } } - public PipController(Context context, + protected PipController(Context context, DisplayController displayController, PipAppOpsListener pipAppOpsListener, PipBoundsHandler pipBoundsHandler, @@ -207,40 +215,27 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac PipTouchHandler pipTouchHandler, WindowManagerShellWrapper windowManagerShellWrapper ) { - mContext = context; - - if (PipUtils.hasSystemFeature(mContext)) { - initController(context, displayController, pipAppOpsListener, pipBoundsHandler, - pipBoundsState, pipMediaController, pipMenuActivityController, pipTaskOrganizer, - pipTouchHandler, windowManagerShellWrapper); - } else { - Log.w(TAG, "Device not support PIP feature"); - } - } - - private void initController(Context context, - DisplayController displayController, - PipAppOpsListener pipAppOpsListener, - PipBoundsHandler pipBoundsHandler, - @NonNull PipBoundsState pipBoundsState, - PipMediaController pipMediaController, - PipMenuActivityController pipMenuActivityController, - PipTaskOrganizer pipTaskOrganizer, - PipTouchHandler pipTouchHandler, - WindowManagerShellWrapper windowManagerShellWrapper) { - // Ensure that we are the primary user's SystemUI. final int processUser = UserManager.get(context).getUserHandle(); if (processUser != UserHandle.USER_SYSTEM) { throw new IllegalStateException("Non-primary Pip component not currently supported."); } + mContext = context; mWindowManagerShellWrapper = windowManagerShellWrapper; mDisplayController = displayController; mPipBoundsHandler = pipBoundsHandler; mPipBoundsState = pipBoundsState; mPipTaskOrganizer = pipTaskOrganizer; mPipTaskOrganizer.registerPipTransitionCallback(this); + mPipTaskOrganizer.registerOnDisplayIdChangeCallback((int displayId) -> { + final DisplayInfo newDisplayInfo = new DisplayInfo(); + displayController.getDisplay(displayId).getDisplayInfo(newDisplayInfo); + mPipBoundsHandler.onDisplayInfoChanged(newDisplayInfo); + updateMovementBounds(null /* toBounds */, false /* fromRotation */, + false /* fromImeAdjustment */, false /* fromShelfAdustment */, + null /* wct */); + }); mMediaController = pipMediaController; mMenuController = pipMenuActivityController; mTouchHandler = pipTouchHandler; @@ -258,7 +253,7 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac mWindowManagerShellWrapper.addPinnedStackListener( new PipControllerPinnedStackListener()); } catch (RemoteException e) { - Log.e(TAG, "Failed to register pinned stack listener", e); + Slog.e(TAG, "Failed to register pinned stack listener", e); } } @@ -403,7 +398,8 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac if (isOutPipDirection(direction)) { // Exiting PIP, save the reentry bounds to restore to when re-entering. updateReentryBounds(pipBounds); - mPipBoundsHandler.onSaveReentryBounds(activity, mReentryBounds); + final float snapFraction = mPipBoundsHandler.getSnapFraction(mReentryBounds); + mPipBoundsState.saveReentryState(mReentryBounds, snapFraction); } // Disable touches while the animation is running mTouchHandler.setTouchEnabled(false); @@ -465,4 +461,24 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac mPipTaskOrganizer.dump(pw, innerPrefix); mPipBoundsState.dump(pw, innerPrefix); } + + /** + * Instantiates {@link PipController}, returns {@code null} if the feature not supported. + */ + @Nullable + public static PipController create(Context context, DisplayController displayController, + PipAppOpsListener pipAppOpsListener, PipBoundsHandler pipBoundsHandler, + PipBoundsState pipBoundsState, PipMediaController pipMediaController, + PipMenuActivityController pipMenuActivityController, + PipTaskOrganizer pipTaskOrganizer, PipTouchHandler pipTouchHandler, + WindowManagerShellWrapper windowManagerShellWrapper) { + if (!context.getPackageManager().hasSystemFeature(FEATURE_PICTURE_IN_PICTURE)) { + Slog.w(TAG, "Device doesn't support Pip feature"); + return null; + } + + return new PipController(context, displayController, pipAppOpsListener, pipBoundsHandler, + pipBoundsState, pipMediaController, pipMenuActivityController, + pipTaskOrganizer, pipTouchHandler, windowManagerShellWrapper); + } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMediaController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMediaController.java index 4a8db6b42b3f..22c05fb8acd9 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMediaController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMediaController.java @@ -28,7 +28,6 @@ import android.content.Intent; import android.content.IntentFilter; import android.graphics.drawable.Icon; import android.media.session.MediaController; -import android.media.session.MediaSession; import android.media.session.MediaSessionManager; import android.media.session.PlaybackState; import android.os.UserHandle; @@ -153,8 +152,7 @@ public class PipMediaController { } ArrayList<RemoteAction> mediaActions = new ArrayList<>(); - int state = mMediaController.getPlaybackState().getState(); - boolean isPlaying = MediaSession.isActiveState(state); + boolean isPlaying = mMediaController.getPlaybackState().isActiveState(); long actions = mMediaController.getPlaybackState().getActions(); // Prev action diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipUtils.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipUtils.java index 6a58ce0d4646..bd2ba32912bc 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipUtils.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipUtils.java @@ -18,7 +18,6 @@ package com.android.wm.shell.pip.phone; import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; -import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE; import android.app.ActivityTaskManager; import android.app.ActivityTaskManager.RootTaskInfo; @@ -30,7 +29,6 @@ import android.util.Log; import android.util.Pair; public class PipUtils { - private static final String TAG = "PipUtils"; /** @@ -58,14 +56,4 @@ public class PipUtils { } return new Pair<>(null, 0); } - - /** - * The util to check if device has PIP feature - * - * @param context application context - * @return true if device has PIP feature, false otherwise. - */ - public static boolean hasSystemFeature(Context context) { - return context.getPackageManager().hasSystemFeature(FEATURE_PICTURE_IN_PICTURE); - } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java b/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java index a0ce9dabffe6..f3dadfcb933a 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java @@ -26,7 +26,7 @@ import com.android.internal.protolog.common.IProtoLogGroup; public enum ShellProtoLogGroup implements IProtoLogGroup { // NOTE: Since we enable these from the same WM ShellCommand, these names should not conflict // with those in the framework ProtoLogGroup - WM_SHELL_TASK_ORG(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false, + WM_SHELL_TASK_ORG(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, true, Consts.TAG_WM_SHELL), WM_SHELL_TRANSITIONS(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, true, Consts.TAG_WM_SHELL), diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTaskOrganizer.java index 8660702a2509..0a1aadc90a62 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTaskOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTaskOrganizer.java @@ -24,6 +24,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECOND import static android.view.Display.DEFAULT_DISPLAY; import static com.android.wm.shell.ShellTaskOrganizer.TASK_LISTENER_TYPE_SPLIT_SCREEN; +import static com.android.wm.shell.ShellTaskOrganizer.taskListenerTypeToString; import android.app.ActivityManager.RunningTaskInfo; import android.graphics.Rect; @@ -32,9 +33,14 @@ import android.util.Log; import android.view.Display; import android.view.SurfaceControl; import android.view.SurfaceSession; +import android.window.TaskAppearedInfo; + +import androidx.annotation.NonNull; import com.android.wm.shell.ShellTaskOrganizer; +import java.io.PrintWriter; + class SplitScreenTaskOrganizer implements ShellTaskOrganizer.TaskListener { private static final String TAG = "SplitScreenTaskOrg"; private static final boolean DEBUG = SplitScreenController.DEBUG; @@ -57,16 +63,21 @@ class SplitScreenTaskOrganizer implements ShellTaskOrganizer.TaskListener { ShellTaskOrganizer shellTaskOrganizer) { mSplitScreenController = splitScreenController; mTaskOrganizer = shellTaskOrganizer; - mTaskOrganizer.addListener(this, TASK_LISTENER_TYPE_SPLIT_SCREEN); + mTaskOrganizer.addListenerForType(this, TASK_LISTENER_TYPE_SPLIT_SCREEN); } void init() throws RemoteException { synchronized (this) { try { - mPrimary = mTaskOrganizer.createRootTask(Display.DEFAULT_DISPLAY, - WINDOWING_MODE_SPLIT_SCREEN_PRIMARY); - mSecondary = mTaskOrganizer.createRootTask(Display.DEFAULT_DISPLAY, - WINDOWING_MODE_SPLIT_SCREEN_SECONDARY); + final TaskAppearedInfo primary = mTaskOrganizer.createRootTask( + Display.DEFAULT_DISPLAY, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, this); + final TaskAppearedInfo secondary = mTaskOrganizer.createRootTask( + Display.DEFAULT_DISPLAY, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, this); + mPrimary = primary.getTaskInfo(); + mPrimarySurface = primary.getLeash(); + mSecondary = secondary.getTaskInfo(); + mSecondarySurface = secondary.getLeash(); + enableSplitScreenSupportIfNeeded(); } catch (Exception e) { // teardown to prevent callbacks mTaskOrganizer.removeListener(this); @@ -87,43 +98,29 @@ class SplitScreenTaskOrganizer implements ShellTaskOrganizer.TaskListener { mSplitScreenController.mTransactionPool.release(t); } - @Override - public void onTaskAppeared(RunningTaskInfo taskInfo, SurfaceControl leash) { - synchronized (this) { - if (mPrimary == null || mSecondary == null) { - Log.w(TAG, "Received onTaskAppeared before creating root tasks " + taskInfo); - return; - } - - if (taskInfo.token.equals(mPrimary.token)) { - mPrimarySurface = leash; - } else if (taskInfo.token.equals(mSecondary.token)) { - mSecondarySurface = leash; - } - - if (!mSplitScreenSupported && mPrimarySurface != null && mSecondarySurface != null) { - mSplitScreenSupported = true; - - // Initialize dim surfaces: - mPrimaryDim = new SurfaceControl.Builder(mSurfaceSession) - .setParent(mPrimarySurface).setColorLayer() - .setName("Primary Divider Dim") - .setCallsite("SplitScreenTaskOrganizer.onTaskAppeared") - .build(); - mSecondaryDim = new SurfaceControl.Builder(mSurfaceSession) - .setParent(mSecondarySurface).setColorLayer() - .setName("Secondary Divider Dim") - .setCallsite("SplitScreenTaskOrganizer.onTaskAppeared") - .build(); - SurfaceControl.Transaction t = getTransaction(); - t.setLayer(mPrimaryDim, Integer.MAX_VALUE); - t.setColor(mPrimaryDim, new float[]{0f, 0f, 0f}); - t.setLayer(mSecondaryDim, Integer.MAX_VALUE); - t.setColor(mSecondaryDim, new float[]{0f, 0f, 0f}); - t.apply(); - releaseTransaction(t); - } - } + private void enableSplitScreenSupportIfNeeded() { + if (mSplitScreenSupported || mPrimarySurface == null || mSecondarySurface == null) return; + + mSplitScreenSupported = true; + + // Initialize dim surfaces: + mPrimaryDim = new SurfaceControl.Builder(mSurfaceSession) + .setParent(mPrimarySurface).setColorLayer() + .setName("Primary Divider Dim") + .setCallsite("SplitScreenTaskOrganizer.onTaskAppeared") + .build(); + mSecondaryDim = new SurfaceControl.Builder(mSurfaceSession) + .setParent(mSecondarySurface).setColorLayer() + .setName("Secondary Divider Dim") + .setCallsite("SplitScreenTaskOrganizer.onTaskAppeared") + .build(); + SurfaceControl.Transaction t = getTransaction(); + t.setLayer(mPrimaryDim, Integer.MAX_VALUE); + t.setColor(mPrimaryDim, new float[]{0f, 0f, 0f}); + t.setLayer(mSecondaryDim, Integer.MAX_VALUE); + t.setColor(mSecondaryDim, new float[]{0f, 0f, 0f}); + t.apply(); + releaseTransaction(t); } @Override @@ -229,4 +226,16 @@ class SplitScreenTaskOrganizer implements ShellTaskOrganizer.TaskListener { mSplitScreenController.ensureNormalSplit(); } } + + @Override + public void dump(@NonNull PrintWriter pw, String prefix) { + final String innerPrefix = prefix + " "; + final String childPrefix = innerPrefix + " "; + pw.println(prefix + this); + } + + @Override + public String toString() { + return TAG + ":" + taskListenerTypeToString(TASK_LISTENER_TYPE_SPLIT_SCREEN); + } } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java index f01fc517d5ba..07a6bda239c7 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTaskOrganizerTests.java @@ -22,6 +22,10 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static com.android.wm.shell.ShellTaskOrganizer.TASK_LISTENER_TYPE_MULTI_WINDOW; import static com.android.wm.shell.ShellTaskOrganizer.TASK_LISTENER_TYPE_PIP; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; +import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy; + +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.any; @@ -29,10 +33,14 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import android.app.ActivityManager.RunningTaskInfo; +import android.content.pm.ParceledListSlice; +import android.os.Binder; +import android.os.IBinder; import android.os.RemoteException; import android.view.SurfaceControl; import android.window.ITaskOrganizer; import android.window.ITaskOrganizerController; +import android.window.TaskAppearedInfo; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; @@ -83,18 +91,17 @@ public class ShellTaskOrganizerTests { public void onTaskVanished(RunningTaskInfo taskInfo) { vanished.add(taskInfo); } - - @Override - public void onBackPressedOnTaskRoot(RunningTaskInfo taskInfo) { - // Not currently used - } } @Before public void setUp() { MockitoAnnotations.initMocks(this); - mOrganizer = new ShellTaskOrganizer(mTaskOrganizerController, mSyncTransactionQueue, - mTransactionPool, mTestExecutor, mTestExecutor); + try { + doReturn(ParceledListSlice.<TaskAppearedInfo>emptyList()) + .when(mTaskOrganizerController).registerTaskOrganizer(any()); + } catch (RemoteException e) {} + mOrganizer = spy(new ShellTaskOrganizer(mTaskOrganizerController, mSyncTransactionQueue, + mTransactionPool, mTestExecutor, mTestExecutor)); } @Test @@ -106,9 +113,10 @@ public class ShellTaskOrganizerTests { @Test public void testOneListenerPerType() { - mOrganizer.addListener(new TrackingTaskListener(), TASK_LISTENER_TYPE_MULTI_WINDOW); + mOrganizer.addListenerForType(new TrackingTaskListener(), TASK_LISTENER_TYPE_MULTI_WINDOW); try { - mOrganizer.addListener(new TrackingTaskListener(), TASK_LISTENER_TYPE_MULTI_WINDOW); + mOrganizer.addListenerForType( + new TrackingTaskListener(), TASK_LISTENER_TYPE_MULTI_WINDOW); fail("Expected exception due to already registered listener"); } catch (Exception e) { // Expected failure @@ -116,10 +124,31 @@ public class ShellTaskOrganizerTests { } @Test + public void testRegisterWithExistingTasks() throws RemoteException { + // Setup some tasks + RunningTaskInfo task1 = createTaskInfo(1, WINDOWING_MODE_MULTI_WINDOW); + RunningTaskInfo task2 = createTaskInfo(2, WINDOWING_MODE_MULTI_WINDOW); + ArrayList<TaskAppearedInfo> taskInfos = new ArrayList<>(); + taskInfos.add(new TaskAppearedInfo(task1, new SurfaceControl())); + taskInfos.add(new TaskAppearedInfo(task2, new SurfaceControl())); + doReturn(new ParceledListSlice(taskInfos)) + .when(mTaskOrganizerController).registerTaskOrganizer(any()); + + // Register and expect the tasks to be stored + mOrganizer.registerOrganizer(); + + // Check that the tasks are next reported when the listener is added + TrackingTaskListener listener = new TrackingTaskListener(); + mOrganizer.addListenerForType(listener, TASK_LISTENER_TYPE_MULTI_WINDOW); + assertTrue(listener.appeared.contains(task1)); + assertTrue(listener.appeared.contains(task2)); + } + + @Test public void testAppearedVanished() { - RunningTaskInfo taskInfo = createTaskInfo(WINDOWING_MODE_MULTI_WINDOW); + RunningTaskInfo taskInfo = createTaskInfo(1, WINDOWING_MODE_MULTI_WINDOW); TrackingTaskListener listener = new TrackingTaskListener(); - mOrganizer.addListener(listener, TASK_LISTENER_TYPE_MULTI_WINDOW); + mOrganizer.addListenerForType(listener, TASK_LISTENER_TYPE_MULTI_WINDOW); mOrganizer.onTaskAppeared(taskInfo, null); assertTrue(listener.appeared.contains(taskInfo)); @@ -129,33 +158,93 @@ public class ShellTaskOrganizerTests { @Test public void testAddListenerExistingTasks() { - RunningTaskInfo taskInfo = createTaskInfo(WINDOWING_MODE_MULTI_WINDOW); + RunningTaskInfo taskInfo = createTaskInfo(1, WINDOWING_MODE_MULTI_WINDOW); mOrganizer.onTaskAppeared(taskInfo, null); TrackingTaskListener listener = new TrackingTaskListener(); - mOrganizer.addListener(listener, TASK_LISTENER_TYPE_MULTI_WINDOW); + mOrganizer.addListenerForType(listener, TASK_LISTENER_TYPE_MULTI_WINDOW); assertTrue(listener.appeared.contains(taskInfo)); } @Test public void testWindowingModeChange() { - RunningTaskInfo taskInfo = createTaskInfo(WINDOWING_MODE_MULTI_WINDOW); + RunningTaskInfo taskInfo = createTaskInfo(1, WINDOWING_MODE_MULTI_WINDOW); TrackingTaskListener mwListener = new TrackingTaskListener(); TrackingTaskListener pipListener = new TrackingTaskListener(); - mOrganizer.addListener(mwListener, TASK_LISTENER_TYPE_MULTI_WINDOW); - mOrganizer.addListener(pipListener, TASK_LISTENER_TYPE_PIP); + mOrganizer.addListenerForType(mwListener, TASK_LISTENER_TYPE_MULTI_WINDOW); + mOrganizer.addListenerForType(pipListener, TASK_LISTENER_TYPE_PIP); mOrganizer.onTaskAppeared(taskInfo, null); assertTrue(mwListener.appeared.contains(taskInfo)); assertTrue(pipListener.appeared.isEmpty()); - taskInfo = createTaskInfo(WINDOWING_MODE_PINNED); + taskInfo = createTaskInfo(1, WINDOWING_MODE_PINNED); mOrganizer.onTaskInfoChanged(taskInfo); assertTrue(mwListener.vanished.contains(taskInfo)); assertTrue(pipListener.appeared.contains(taskInfo)); } - private RunningTaskInfo createTaskInfo(int windowingMode) { + @Test + public void testAddListenerForTaskId_afterTypeListener() { + RunningTaskInfo task1 = createTaskInfo(1, WINDOWING_MODE_MULTI_WINDOW); + TrackingTaskListener mwListener = new TrackingTaskListener(); + TrackingTaskListener task1Listener = new TrackingTaskListener(); + mOrganizer.addListenerForType(mwListener, TASK_LISTENER_TYPE_MULTI_WINDOW); + mOrganizer.onTaskAppeared(task1, null); + assertTrue(mwListener.appeared.contains(task1)); + + // Add task 1 specific listener + mOrganizer.addListenerForTaskId(task1Listener, 1); + assertTrue(mwListener.vanished.contains(task1)); + assertTrue(task1Listener.appeared.contains(task1)); + } + + @Test + public void testAddListenerForTaskId_beforeTypeListener() { + RunningTaskInfo task1 = createTaskInfo(1, WINDOWING_MODE_MULTI_WINDOW); + TrackingTaskListener mwListener = new TrackingTaskListener(); + TrackingTaskListener task1Listener = new TrackingTaskListener(); + mOrganizer.onTaskAppeared(task1, null); + mOrganizer.addListenerForTaskId(task1Listener, 1); + assertTrue(task1Listener.appeared.contains(task1)); + + mOrganizer.addListenerForType(mwListener, TASK_LISTENER_TYPE_MULTI_WINDOW); + assertFalse(mwListener.appeared.contains(task1)); + } + + @Test + public void testGetTaskListener() { + RunningTaskInfo task1 = createTaskInfo(1, WINDOWING_MODE_MULTI_WINDOW); + + TrackingTaskListener mwListener = new TrackingTaskListener(); + mOrganizer.addListenerForType(mwListener, TASK_LISTENER_TYPE_MULTI_WINDOW); + + TrackingTaskListener cookieListener = new TrackingTaskListener(); + IBinder cookie = new Binder(); + task1.addLaunchCookie(cookie); + mOrganizer.setPendingLaunchCookieListener(cookie, cookieListener); + + // Priority goes to the cookie listener so we would expect the task appear to show up there + // instead of the multi-window type listener. + mOrganizer.onTaskAppeared(task1, null); + assertTrue(cookieListener.appeared.contains(task1)); + assertFalse(mwListener.appeared.contains(task1)); + + TrackingTaskListener task1Listener = new TrackingTaskListener(); + + boolean gotException = false; + try { + mOrganizer.addListenerForTaskId(task1Listener, 1); + } catch (Exception e) { + gotException = true; + } + // It should not be possible to add a task id listener for a task already mapped to a + // listener through cookie. + assertTrue(gotException); + } + + private RunningTaskInfo createTaskInfo(int taskId, int windowingMode) { RunningTaskInfo taskInfo = new RunningTaskInfo(); + taskInfo.taskId = taskId; taskInfo.configuration.windowConfiguration.setWindowingMode(windowingMode); return taskInfo; } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsHandlerTest.java index d9e3148913b3..e0ac8e24756d 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsHandlerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsHandlerTest.java @@ -20,7 +20,6 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; -import android.content.ComponentName; import android.graphics.Rect; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; @@ -58,15 +57,13 @@ public class PipBoundsHandlerTest extends PipTestCase { private PipBoundsHandler mPipBoundsHandler; private DisplayInfo mDefaultDisplayInfo; - private ComponentName mTestComponentName1; - private ComponentName mTestComponentName2; + private PipBoundsState mPipBoundsState; @Before public void setUp() throws Exception { initializeMockResources(); - mPipBoundsHandler = new PipBoundsHandler(mContext); - mTestComponentName1 = new ComponentName(mContext, "component1"); - mTestComponentName2 = new ComponentName(mContext, "component2"); + mPipBoundsState = new PipBoundsState(); + mPipBoundsHandler = new PipBoundsHandler(mContext, mPipBoundsState); mPipBoundsHandler.onDisplayInfoChanged(mDefaultDisplayInfo); } @@ -126,8 +123,8 @@ public class PipBoundsHandlerTest extends PipTestCase { (MAX_ASPECT_RATIO + DEFAULT_ASPECT_RATIO) / 2 }; for (float aspectRatio : aspectRatios) { - final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds( - mTestComponentName1, aspectRatio, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE); + final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(aspectRatio, + EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE); final float actualAspectRatio = destinationBounds.width() / (destinationBounds.height() * 1f); assertEquals("Destination bounds matches the given aspect ratio", @@ -142,8 +139,8 @@ public class PipBoundsHandlerTest extends PipTestCase { MAX_ASPECT_RATIO * 2 }; for (float aspectRatio : invalidAspectRatios) { - final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds( - mTestComponentName1, aspectRatio, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE); + final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(aspectRatio, + EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE); final float actualAspectRatio = destinationBounds.width() / (destinationBounds.height() * 1f); assertEquals("Destination bounds fallbacks to default aspect ratio", @@ -158,8 +155,8 @@ public class PipBoundsHandlerTest extends PipTestCase { final Rect currentBounds = new Rect(0, 0, 0, 100); currentBounds.right = (int) (currentBounds.height() * aspectRatio) + currentBounds.left; - final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds( - mTestComponentName1, aspectRatio, currentBounds, EMPTY_MINIMAL_SIZE); + final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(aspectRatio, + currentBounds, EMPTY_MINIMAL_SIZE); final float actualAspectRatio = destinationBounds.width() / (destinationBounds.height() * 1f); @@ -182,8 +179,8 @@ public class PipBoundsHandlerTest extends PipTestCase { for (int i = 0; i < aspectRatios.length; i++) { final float aspectRatio = aspectRatios[i]; final Size minimalSize = minimalSizes[i]; - final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds( - mTestComponentName1, aspectRatio, EMPTY_CURRENT_BOUNDS, minimalSize); + final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(aspectRatio, + EMPTY_CURRENT_BOUNDS, minimalSize); assertTrue("Destination bounds is no smaller than minimal requirement", (destinationBounds.width() == minimalSize.getWidth() && destinationBounds.height() >= minimalSize.getHeight()) @@ -203,8 +200,8 @@ public class PipBoundsHandlerTest extends PipTestCase { currentBounds.right = (int) (currentBounds.height() * aspectRatio) + currentBounds.left; final Size minSize = new Size(currentBounds.width() / 2, currentBounds.height() / 2); - final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds( - mTestComponentName1, aspectRatio, currentBounds, minSize); + final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(aspectRatio, + currentBounds, minSize); assertTrue("Destination bounds ignores minimal size", destinationBounds.width() > minSize.getWidth() @@ -212,28 +209,44 @@ public class PipBoundsHandlerTest extends PipTestCase { } @Test - public void getDestinationBounds_withDifferentComponentName_ignoreLastPosition() { - final Rect oldPosition = mPipBoundsHandler.getDestinationBounds(mTestComponentName1, - DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE); + public void getDestinationBounds_reentryStateExists_restoreLastSize() { + final Rect reentryBounds = mPipBoundsHandler.getDestinationBounds(DEFAULT_ASPECT_RATIO, + EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE); + reentryBounds.scale(1.25f); + final float reentrySnapFraction = mPipBoundsHandler.getSnapFraction(reentryBounds); + + mPipBoundsState.saveReentryState(reentryBounds, reentrySnapFraction); + final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(DEFAULT_ASPECT_RATIO, + EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE); + + assertEquals(reentryBounds.width(), destinationBounds.width()); + assertEquals(reentryBounds.height(), destinationBounds.height()); + } + + @Test + public void getDestinationBounds_reentryStateExists_restoreLastPosition() { + final Rect reentryBounds = mPipBoundsHandler.getDestinationBounds(DEFAULT_ASPECT_RATIO, + EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE); + reentryBounds.offset(0, -100); + final float reentrySnapFraction = mPipBoundsHandler.getSnapFraction(reentryBounds); - oldPosition.offset(0, -100); - mPipBoundsHandler.onSaveReentryBounds(mTestComponentName1, oldPosition); + mPipBoundsState.saveReentryState(reentryBounds, reentrySnapFraction); - final Rect newPosition = mPipBoundsHandler.getDestinationBounds(mTestComponentName2, - DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE); + final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(DEFAULT_ASPECT_RATIO, + EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE); - assertNonBoundsInclusionWithMargin("ignore saved bounds", oldPosition, newPosition); + assertBoundsInclusionWithMargin("restoreLastPosition", reentryBounds, destinationBounds); } @Test public void setShelfHeight_offsetBounds() { final int shelfHeight = 100; - final Rect oldPosition = mPipBoundsHandler.getDestinationBounds(mTestComponentName1, - DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE); + final Rect oldPosition = mPipBoundsHandler.getDestinationBounds(DEFAULT_ASPECT_RATIO, + EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE); mPipBoundsHandler.setShelfHeight(true, shelfHeight); - final Rect newPosition = mPipBoundsHandler.getDestinationBounds(mTestComponentName1, - DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE); + final Rect newPosition = mPipBoundsHandler.getDestinationBounds(DEFAULT_ASPECT_RATIO, + EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE); oldPosition.offset(0, -shelfHeight); assertBoundsInclusionWithMargin("offsetBounds by shelf", oldPosition, newPosition); @@ -242,92 +255,30 @@ public class PipBoundsHandlerTest extends PipTestCase { @Test public void onImeVisibilityChanged_offsetBounds() { final int imeHeight = 100; - final Rect oldPosition = mPipBoundsHandler.getDestinationBounds(mTestComponentName1, - DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE); + final Rect oldPosition = mPipBoundsHandler.getDestinationBounds(DEFAULT_ASPECT_RATIO, + EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE); mPipBoundsHandler.onImeVisibilityChanged(true, imeHeight); - final Rect newPosition = mPipBoundsHandler.getDestinationBounds(mTestComponentName1, - DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE); + final Rect newPosition = mPipBoundsHandler.getDestinationBounds(DEFAULT_ASPECT_RATIO, + EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE); oldPosition.offset(0, -imeHeight); assertBoundsInclusionWithMargin("offsetBounds by IME", oldPosition, newPosition); } @Test - public void onSaveReentryBounds_restoreLastPosition() { - final Rect oldPosition = mPipBoundsHandler.getDestinationBounds(mTestComponentName1, - DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE); - - oldPosition.offset(0, -100); - mPipBoundsHandler.onSaveReentryBounds(mTestComponentName1, oldPosition); - - final Rect newPosition = mPipBoundsHandler.getDestinationBounds(mTestComponentName1, - DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE); - - assertBoundsInclusionWithMargin("restoreLastPosition", oldPosition, newPosition); - } - - @Test - public void onSaveReentryBounds_restoreLastSize() { - final Rect oldSize = mPipBoundsHandler.getDestinationBounds(mTestComponentName1, - DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE); + public void getDestinationBounds_noReentryState_useDefaultBounds() { + final Rect defaultBounds = mPipBoundsHandler.getDestinationBounds(DEFAULT_ASPECT_RATIO, + EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE); - oldSize.scale(1.25f); - mPipBoundsHandler.onSaveReentryBounds(mTestComponentName1, oldSize); + mPipBoundsState.clearReentryState(); - final Rect newSize = mPipBoundsHandler.getDestinationBounds(mTestComponentName1, - DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE); - - assertEquals(oldSize.width(), newSize.width()); - assertEquals(oldSize.height(), newSize.height()); - } - - @Test - public void onResetReentryBounds_useDefaultBounds() { - final Rect defaultBounds = mPipBoundsHandler.getDestinationBounds(mTestComponentName1, - DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE); - final Rect newBounds = new Rect(defaultBounds); - newBounds.offset(0, -100); - mPipBoundsHandler.onSaveReentryBounds(mTestComponentName1, newBounds); - - mPipBoundsHandler.onResetReentryBounds(mTestComponentName1); - final Rect actualBounds = mPipBoundsHandler.getDestinationBounds(mTestComponentName1, - DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE); + final Rect actualBounds = mPipBoundsHandler.getDestinationBounds(DEFAULT_ASPECT_RATIO, + EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE); assertBoundsInclusionWithMargin("useDefaultBounds", defaultBounds, actualBounds); } - @Test - public void onResetReentryBounds_componentMismatch_restoreLastPosition() { - final Rect defaultBounds = mPipBoundsHandler.getDestinationBounds(mTestComponentName1, - DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE); - final Rect newBounds = new Rect(defaultBounds); - newBounds.offset(0, -100); - mPipBoundsHandler.onSaveReentryBounds(mTestComponentName1, newBounds); - - mPipBoundsHandler.onResetReentryBounds(mTestComponentName2); - final Rect actualBounds = mPipBoundsHandler.getDestinationBounds(mTestComponentName1, - DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE); - - assertBoundsInclusionWithMargin("restoreLastPosition", newBounds, actualBounds); - } - - @Test - public void onSaveReentryBounds_componentMismatch_restoreLastSize() { - final Rect oldSize = mPipBoundsHandler.getDestinationBounds(mTestComponentName1, - DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE); - - oldSize.scale(1.25f); - mPipBoundsHandler.onSaveReentryBounds(mTestComponentName1, oldSize); - - mPipBoundsHandler.onResetReentryBounds(mTestComponentName2); - final Rect newSize = mPipBoundsHandler.getDestinationBounds(mTestComponentName1, - DEFAULT_ASPECT_RATIO, EMPTY_CURRENT_BOUNDS, EMPTY_MINIMAL_SIZE); - - assertEquals(oldSize.width(), newSize.width()); - assertEquals(oldSize.height(), newSize.height()); - } - private void assertBoundsInclusionWithMargin(String from, Rect expected, Rect actual) { final Rect expectedWithMargin = new Rect(expected); expectedWithMargin.inset(-ROUNDING_ERROR_MARGIN, -ROUNDING_ERROR_MARGIN); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsStateTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsStateTest.java new file mode 100644 index 000000000000..dc9399edaa3b --- /dev/null +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsStateTest.java @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.pip; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; + +import android.content.ComponentName; +import android.graphics.Rect; +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; +import android.util.Size; + +import androidx.test.filters.SmallTest; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +/** + * Tests for {@link PipBoundsState}. + */ +@RunWith(AndroidTestingRunner.class) +@TestableLooper.RunWithLooper +@SmallTest +public class PipBoundsStateTest extends PipTestCase { + + private static final Rect DEFAULT_BOUNDS = new Rect(0, 0, 10, 10); + private static final float DEFAULT_SNAP_FRACTION = 1.0f; + + private PipBoundsState mPipBoundsState; + private ComponentName mTestComponentName1; + private ComponentName mTestComponentName2; + + @Before + public void setUp() { + mPipBoundsState = new PipBoundsState(); + mTestComponentName1 = new ComponentName(mContext, "component1"); + mTestComponentName2 = new ComponentName(mContext, "component2"); + } + + @Test + public void testSetBounds() { + final Rect bounds = new Rect(0, 0, 100, 100); + mPipBoundsState.setBounds(bounds); + + assertEquals(bounds, mPipBoundsState.getBounds()); + } + + @Test + public void testSetReentryState() { + final Rect bounds = new Rect(0, 0, 100, 100); + final float snapFraction = 0.5f; + + mPipBoundsState.saveReentryState(bounds, snapFraction); + + final PipBoundsState.PipReentryState state = mPipBoundsState.getReentryState(); + assertEquals(new Size(100, 100), state.getSize()); + assertEquals(snapFraction, state.getSnapFraction(), 0.01); + } + + @Test + public void testClearReentryState() { + final Rect bounds = new Rect(0, 0, 100, 100); + final float snapFraction = 0.5f; + + mPipBoundsState.saveReentryState(bounds, snapFraction); + mPipBoundsState.clearReentryState(); + + assertNull(mPipBoundsState.getReentryState()); + } + + @Test + public void testSetLastPipComponentName_notChanged_doesNotClearReentryState() { + mPipBoundsState.setLastPipComponentName(mTestComponentName1); + mPipBoundsState.saveReentryState(DEFAULT_BOUNDS, DEFAULT_SNAP_FRACTION); + + mPipBoundsState.setLastPipComponentName(mTestComponentName1); + + final PipBoundsState.PipReentryState state = mPipBoundsState.getReentryState(); + assertNotNull(state); + assertEquals(new Size(DEFAULT_BOUNDS.width(), DEFAULT_BOUNDS.height()), state.getSize()); + assertEquals(DEFAULT_SNAP_FRACTION, state.getSnapFraction(), 0.01); + } + + @Test + public void testSetLastPipComponentName_changed_clearReentryState() { + mPipBoundsState.setLastPipComponentName(mTestComponentName1); + mPipBoundsState.saveReentryState(DEFAULT_BOUNDS, DEFAULT_SNAP_FRACTION); + + mPipBoundsState.setLastPipComponentName(mTestComponentName2); + + assertNull(mPipBoundsState.getReentryState()); + } +} diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTaskOrganizerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java index 37b93bcdd051..54543d25b401 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTaskOrganizerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java @@ -14,32 +14,19 @@ * limitations under the License. */ -package com.android.wm.shell.pip.phone; - -import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE; +package com.android.wm.shell.pip; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; -import android.content.pm.PackageManager; import android.os.RemoteException; import android.test.suitebuilder.annotation.SmallTest; import android.testing.AndroidTestingRunner; -import android.testing.TestableContext; import android.testing.TestableLooper; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.common.DisplayController; -import com.android.wm.shell.pip.PipBoundsHandler; -import com.android.wm.shell.pip.PipBoundsState; -import com.android.wm.shell.pip.PipSurfaceTransactionHelper; -import com.android.wm.shell.pip.PipTaskOrganizer; -import com.android.wm.shell.pip.PipTestCase; -import com.android.wm.shell.pip.PipUiEventLogger; import com.android.wm.shell.splitscreen.SplitScreen; import org.junit.Before; @@ -58,39 +45,30 @@ import java.util.Optional; @TestableLooper.RunWithLooper public class PipTaskOrganizerTest extends PipTestCase { private PipTaskOrganizer mSpiedPipTaskOrganizer; - private TestableContext mSpiedContext; @Mock private DisplayController mMockdDisplayController; - @Mock private PackageManager mPackageManager; @Mock private PipBoundsHandler mMockPipBoundsHandler; @Mock private PipSurfaceTransactionHelper mMockPipSurfaceTransactionHelper; @Mock private PipUiEventLogger mMockPipUiEventLogger; @Mock private Optional<SplitScreen> mMockOptionalSplitScreen; @Mock private ShellTaskOrganizer mMockShellTaskOrganizer; - private PipBoundsState mPipBoundsState; + @Mock private PipBoundsState mMockPipBoundsState; @Before public void setUp() throws RemoteException { MockitoAnnotations.initMocks(this); - mPipBoundsState = new PipBoundsState(); - - mSpiedContext = spy(mContext); - - when(mPackageManager.hasSystemFeature(FEATURE_PICTURE_IN_PICTURE)).thenReturn(false); - when(mSpiedContext.getPackageManager()).thenReturn(mPackageManager); - - mSpiedPipTaskOrganizer = spy(new PipTaskOrganizer(mSpiedContext, mPipBoundsState, + mSpiedPipTaskOrganizer = new PipTaskOrganizer(mContext, mMockPipBoundsState, mMockPipBoundsHandler, mMockPipSurfaceTransactionHelper, mMockOptionalSplitScreen, - mMockdDisplayController, mMockPipUiEventLogger, mMockShellTaskOrganizer)); + mMockdDisplayController, mMockPipUiEventLogger, mMockShellTaskOrganizer); } @Test - public void testNonPipDevice_shellTaskOrganizer_shouldNotAddListener() { - verify(mMockShellTaskOrganizer, never()).addListener(any(), anyInt()); + public void instantiatePipTaskOrganizer_addsTaskListener() { + verify(mMockShellTaskOrganizer).addListenerForType(any(), anyInt()); } @Test - public void testNonPipDevice_displayController_shouldNotAddDisplayWindowListener() { - verify(mMockdDisplayController, never()).addDisplayWindowListener(any()); + public void instantiatePipTaskOrganizer_addsDisplayWindowListener() { + verify(mMockdDisplayController).addDisplayWindowListener(any()); } } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java index 2b987e98bc7e..a282a48e8494 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java @@ -18,17 +18,18 @@ package com.android.wm.shell.pip.phone; import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE; +import static org.junit.Assert.assertNull; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.never; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.content.Context; import android.content.pm.PackageManager; import android.os.RemoteException; import android.test.suitebuilder.annotation.SmallTest; import android.testing.AndroidTestingRunner; -import android.testing.TestableContext; import android.testing.TestableLooper; import com.android.wm.shell.WindowManagerShellWrapper; @@ -37,10 +38,6 @@ import com.android.wm.shell.pip.PipBoundsHandler; import com.android.wm.shell.pip.PipBoundsState; import com.android.wm.shell.pip.PipTaskOrganizer; import com.android.wm.shell.pip.PipTestCase; -import com.android.wm.shell.pip.phone.PipAppOpsListener; -import com.android.wm.shell.pip.phone.PipController; -import com.android.wm.shell.pip.phone.PipMediaController; -import com.android.wm.shell.pip.phone.PipTouchHandler; import org.junit.Before; import org.junit.Test; @@ -55,49 +52,52 @@ import org.mockito.MockitoAnnotations; @RunWith(AndroidTestingRunner.class) @TestableLooper.RunWithLooper public class PipControllerTest extends PipTestCase { - private com.android.wm.shell.pip.phone.PipController mPipController; - private TestableContext mSpiedContext; + private PipController mPipController; - @Mock private DisplayController mMockdDisplayController; - @Mock private PackageManager mPackageManager; - @Mock private com.android.wm.shell.pip.phone.PipMenuActivityController - mMockPipMenuActivityController; + @Mock private DisplayController mMockDisplayController; + @Mock private PipMenuActivityController mMockPipMenuActivityController; @Mock private PipAppOpsListener mMockPipAppOpsListener; @Mock private PipBoundsHandler mMockPipBoundsHandler; @Mock private PipMediaController mMockPipMediaController; @Mock private PipTaskOrganizer mMockPipTaskOrganizer; @Mock private PipTouchHandler mMockPipTouchHandler; @Mock private WindowManagerShellWrapper mMockWindowManagerShellWrapper; - private PipBoundsState mPipBoundsState; + @Mock private PipBoundsState mMockPipBoundsState; @Before public void setUp() throws RemoteException { MockitoAnnotations.initMocks(this); - mPipBoundsState = new PipBoundsState(); - - mSpiedContext = spy(mContext); - - when(mPackageManager.hasSystemFeature(FEATURE_PICTURE_IN_PICTURE)).thenReturn(false); - when(mSpiedContext.getPackageManager()).thenReturn(mPackageManager); - - mPipController = new PipController(mSpiedContext, mMockdDisplayController, - mMockPipAppOpsListener, mMockPipBoundsHandler, mPipBoundsState, + mPipController = new PipController(mContext, mMockDisplayController, + mMockPipAppOpsListener, mMockPipBoundsHandler, mMockPipBoundsState, mMockPipMediaController, mMockPipMenuActivityController, mMockPipTaskOrganizer, mMockPipTouchHandler, mMockWindowManagerShellWrapper); } @Test - public void testNonPipDevice_shouldNotRegisterPipTransitionCallback() { - verify(mMockPipTaskOrganizer, never()).registerPipTransitionCallback(any()); + public void instantiatePipController_registersPipTransitionCallback() { + verify(mMockPipTaskOrganizer).registerPipTransitionCallback(any()); + } + + @Test + public void instantiatePipController_addsDisplayChangingController() { + verify(mMockDisplayController).addDisplayChangingController(any()); } @Test - public void testNonPipDevice_shouldNotAddDisplayChangingController() { - verify(mMockdDisplayController, never()).addDisplayChangingController(any()); + public void instantiatePipController_addsDisplayWindowListener() { + verify(mMockDisplayController).addDisplayWindowListener(any()); } @Test - public void testNonPipDevice_shouldNotAddDisplayWindowListener() { - verify(mMockdDisplayController, never()).addDisplayWindowListener(any()); + public void createPip_notSupported_returnsNull() { + Context spyContext = spy(mContext); + PackageManager mockPackageManager = mock(PackageManager.class); + when(mockPackageManager.hasSystemFeature(FEATURE_PICTURE_IN_PICTURE)).thenReturn(false); + when(spyContext.getPackageManager()).thenReturn(mockPackageManager); + + assertNull(PipController.create(spyContext, mMockDisplayController, + mMockPipAppOpsListener, mMockPipBoundsHandler, mMockPipBoundsState, + mMockPipMediaController, mMockPipMenuActivityController, mMockPipTaskOrganizer, + mMockPipTouchHandler, mMockWindowManagerShellWrapper)); } } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java index 4713142118a8..3f60cc01f20b 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java @@ -93,7 +93,7 @@ public class PipTouchHandlerTest extends PipTestCase { public void setUp() throws Exception { MockitoAnnotations.initMocks(this); mPipBoundsState = new PipBoundsState(); - mPipBoundsHandler = new PipBoundsHandler(mContext); + mPipBoundsHandler = new PipBoundsHandler(mContext, mPipBoundsState); mPipSnapAlgorithm = mPipBoundsHandler.getSnapAlgorithm(); mPipSnapAlgorithm = new PipSnapAlgorithm(mContext); mPipTouchHandler = new PipTouchHandler(mContext, mPipMenuActivityController, diff --git a/libs/androidfw/Android.bp b/libs/androidfw/Android.bp index 8ab7da5257ab..903ca2aa0783 100644 --- a/libs/androidfw/Android.bp +++ b/libs/androidfw/Android.bp @@ -155,11 +155,12 @@ cc_test { android: { srcs: [ "tests/BackupData_test.cpp", - "tests/BackupHelpers_test.cpp", + "tests/BackupHelpers_test.cpp", + "tests/CursorWindow_test.cpp", "tests/ObbFile_test.cpp", "tests/PosixUtils_test.cpp", ], - shared_libs: common_test_libs + ["libui"], + shared_libs: common_test_libs + ["libbinder", "liblog", "libui"], }, host: { static_libs: common_test_libs + ["liblog", "libz"], @@ -185,9 +186,28 @@ cc_benchmark { // Actual benchmarks. "tests/AssetManager2_bench.cpp", "tests/AttributeResolution_bench.cpp", + "tests/CursorWindow_bench.cpp", "tests/SparseEntry_bench.cpp", "tests/Theme_bench.cpp", ], shared_libs: common_test_libs, data: ["tests/data/**/*.apk"], } + +cc_library { + name: "libandroidfw_fuzzer_lib", + defaults: ["libandroidfw_defaults"], + host_supported: true, + srcs: [ + "CursorWindow.cpp", + ], + export_include_dirs: ["include"], + target: { + android: { + shared_libs: common_test_libs + ["libbinder", "liblog"], + }, + host: { + static_libs: common_test_libs + ["libbinder", "liblog"], + }, + }, +} diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp index b9765ea7212c..99dd3134ff8a 100644 --- a/libs/androidfw/AssetManager2.cpp +++ b/libs/androidfw/AssetManager2.cpp @@ -39,10 +39,8 @@ namespace android { struct FindEntryResult { - // A pointer to the resource table entry for this resource. - // If the size of the entry is > sizeof(ResTable_entry), it can be cast to - // a ResTable_map_entry and processed as a bag/map. - ResTable_entry_handle entry; + // A pointer to the value of the resource table entry. + std::variant<Res_value, const ResTable_map_entry*> entry; // The configuration for which the resulting entry was defined. This is already swapped to host // endianness. @@ -554,11 +552,9 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri if (!overlay_entry) { // No id map entry exists for this target resource. continue; - } - - if (overlay_entry.IsTableEntry()) { + } else if (overlay_entry.IsInlineValue()) { // The target resource is overlaid by an inline value not represented by a resource. - out_entry->entry = overlay_entry.GetTableEntry(); + out_entry->entry = overlay_entry.GetInlineValue(); out_entry->dynamic_ref_table = id_map.overlay_res_maps_.GetOverlayDynamicRefTable(); cookie = id_map.cookie; continue; @@ -580,7 +576,7 @@ ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_overri } cookie = overlay_cookie; - out_entry->entry = std::move(overlay_result.entry); + out_entry->entry = overlay_result.entry; out_entry->config = overlay_result.config; out_entry->dynamic_ref_table = id_map.overlay_res_maps_.GetOverlayDynamicRefTable(); if (resource_resolution_logging_enabled_) { @@ -761,7 +757,19 @@ ApkAssetsCookie AssetManager2::FindEntryInternal(const PackageGroup& package_gro return kInvalidCookie; } - out_entry->entry = ResTable_entry_handle::unmanaged(best_entry); + const uint16_t entry_size = dtohs(best_entry->size); + if (entry_size >= sizeof(ResTable_map_entry) && + (dtohs(best_entry->flags) & ResTable_entry::FLAG_COMPLEX)) { + // The entry represents a bag/map. + out_entry->entry = reinterpret_cast<const ResTable_map_entry*>(best_entry); + } else { + // The entry represents a value. + Res_value value; + value.copyFrom_dtoh(*reinterpret_cast<const Res_value*>( + reinterpret_cast<const uint8_t*>(best_entry) + entry_size)); + out_entry->entry = value; + } + out_entry->config = *best_config; out_entry->type_flags = type_flags; out_entry->package_name = &best_package->GetPackageName(); @@ -905,8 +913,8 @@ ApkAssetsCookie AssetManager2::GetResource(uint32_t resid, bool may_be_bag, return kInvalidCookie; } - const ResTable_entry* table_entry = *entry.entry; - if (dtohs(table_entry->flags) & ResTable_entry::FLAG_COMPLEX) { + auto result_map_entry = std::get_if<const ResTable_map_entry*>(&entry.entry); + if (result_map_entry != nullptr) { if (!may_be_bag) { LOG(ERROR) << base::StringPrintf("Resource %08x is a complex map type.", resid); return kInvalidCookie; @@ -920,11 +928,8 @@ ApkAssetsCookie AssetManager2::GetResource(uint32_t resid, bool may_be_bag, return cookie; } - const Res_value* device_value = reinterpret_cast<const Res_value*>( - reinterpret_cast<const uint8_t*>(table_entry) + dtohs(table_entry->size)); - out_value->copyFrom_dtoh(*device_value); - // Convert the package ID to the runtime assigned package ID. + *out_value = std::get<Res_value>(entry.entry); entry.dynamic_ref_table->lookupResourceValue(out_value); *out_selected_config = entry.config; @@ -1004,19 +1009,15 @@ const ResolvedBag* AssetManager2::GetBag(uint32_t resid, std::vector<uint32_t>& return nullptr; } - // Check that the size of the entry header is at least as big as - // the desired ResTable_map_entry. Also verify that the entry - // was intended to be a map. - const ResTable_entry* table_entry = *entry.entry; - if (dtohs(table_entry->size) < sizeof(ResTable_map_entry) || - (dtohs(table_entry->flags) & ResTable_entry::FLAG_COMPLEX) == 0) { + auto result_map_entry = std::get_if<const ResTable_map_entry*>(&entry.entry); + if (result_map_entry == nullptr) { // Not a bag, nothing to do. return nullptr; } - const ResTable_map_entry* map = reinterpret_cast<const ResTable_map_entry*>(table_entry); - const ResTable_map* map_entry = - reinterpret_cast<const ResTable_map*>(reinterpret_cast<const uint8_t*>(map) + map->size); + auto map = reinterpret_cast<const ResTable_map_entry*>(*result_map_entry); + auto map_entry = reinterpret_cast<const ResTable_map*>( + reinterpret_cast<const uint8_t*>(map) + map->size); const ResTable_map* const map_entry_end = map_entry + dtohl(map->count); // Keep track of ids that have already been seen to prevent infinite loops caused by circular diff --git a/libs/androidfw/CursorWindow.cpp b/libs/androidfw/CursorWindow.cpp index 71c8e1f6121f..1b8db46c54b6 100644 --- a/libs/androidfw/CursorWindow.cpp +++ b/libs/androidfw/CursorWindow.cpp @@ -14,19 +14,14 @@ * limitations under the License. */ -#undef LOG_TAG #define LOG_TAG "CursorWindow" #include <androidfw/CursorWindow.h> -#include <binder/Parcel.h> -#include <utils/Log.h> -#include <cutils/ashmem.h> #include <sys/mman.h> -#include <assert.h> -#include <string.h> -#include <stdlib.h> +#include "android-base/logging.h" +#include "cutils/ashmem.h" namespace android { @@ -36,11 +31,10 @@ namespace android { */ static constexpr const size_t kInlineSize = 16384; -CursorWindow::CursorWindow(const String8& name, int ashmemFd, void* data, size_t size, - size_t inflatedSize, bool readOnly) : - mName(name), mAshmemFd(ashmemFd), mData(data), mSize(size), - mInflatedSize(inflatedSize), mReadOnly(readOnly) { - mHeader = static_cast<Header*>(mData); +static constexpr const size_t kSlotShift = 4; +static constexpr const size_t kSlotSizeBytes = 1 << kSlotShift; + +CursorWindow::CursorWindow() { } CursorWindow::~CursorWindow() { @@ -52,234 +46,243 @@ CursorWindow::~CursorWindow() { } } -status_t CursorWindow::create(const String8& name, size_t inflatedSize, - CursorWindow** outCursorWindow) { - *outCursorWindow = nullptr; - - size_t size = std::min(kInlineSize, inflatedSize); - void* data = calloc(size, 1); - if (!data) return NO_MEMORY; - - CursorWindow* window = new CursorWindow(name, -1, data, size, - inflatedSize, false /*readOnly*/); - status_t result = window->clear(); - if (!result) { - LOG_WINDOW("Created new CursorWindow: freeOffset=%d, " - "numRows=%d, numColumns=%d, mSize=%zu, mData=%p", - window->mHeader->freeOffset, - window->mHeader->numRows, - window->mHeader->numColumns, - window->mSize, window->mData); - *outCursorWindow = window; - return OK; - } +status_t CursorWindow::create(const String8 &name, size_t inflatedSize, CursorWindow **outWindow) { + *outWindow = nullptr; + + CursorWindow* window = new CursorWindow(); + if (!window) goto fail; + + window->mName = name; + window->mSize = std::min(kInlineSize, inflatedSize); + window->mInflatedSize = inflatedSize; + window->mData = malloc(window->mSize); + if (!window->mData) goto fail; + window->mReadOnly = false; + + window->clear(); + window->updateSlotsData(); + + LOG(DEBUG) << "Created: " << window->toString(); + *outWindow = window; + return OK; + +fail: + LOG(ERROR) << "Failed create"; +fail_silent: delete window; - return result; + return UNKNOWN_ERROR; } -status_t CursorWindow::inflate() { - // Shortcut when we can't expand any further - if (mSize == mInflatedSize) return INVALID_OPERATION; +status_t CursorWindow::maybeInflate() { + int ashmemFd = 0; + void* newData = nullptr; + + // Bail early when we can't expand any further + if (mReadOnly || mSize == mInflatedSize) { + return INVALID_OPERATION; + } String8 ashmemName("CursorWindow: "); ashmemName.append(mName); - status_t result; - int ashmemFd = ashmem_create_region(ashmemName.string(), mInflatedSize); + ashmemFd = ashmem_create_region(ashmemName.string(), mInflatedSize); if (ashmemFd < 0) { - result = -errno; - ALOGE("CursorWindow: ashmem_create_region() failed: errno=%d.", errno); - } else { - result = ashmem_set_prot_region(ashmemFd, PROT_READ | PROT_WRITE); - if (result < 0) { - ALOGE("CursorWindow: ashmem_set_prot_region() failed: errno=%d",errno); - } else { - void* data = ::mmap(NULL, mInflatedSize, PROT_READ | PROT_WRITE, - MAP_SHARED, ashmemFd, 0); - if (data == MAP_FAILED) { - result = -errno; - ALOGE("CursorWindow: mmap() failed: errno=%d.", errno); - } else { - result = ashmem_set_prot_region(ashmemFd, PROT_READ); - if (result < 0) { - ALOGE("CursorWindow: ashmem_set_prot_region() failed: errno=%d.", errno); - } else { - // Move inline contents into new ashmem region - memcpy(data, mData, mSize); - free(mData); - mAshmemFd = ashmemFd; - mData = data; - mHeader = static_cast<Header*>(mData); - mSize = mInflatedSize; - LOG_WINDOW("Inflated CursorWindow: freeOffset=%d, " - "numRows=%d, numColumns=%d, mSize=%zu, mData=%p", - mHeader->freeOffset, - mHeader->numRows, - mHeader->numColumns, - mSize, mData); - return OK; - } - } - ::munmap(data, mInflatedSize); - } - ::close(ashmemFd); + PLOG(ERROR) << "Failed ashmem_create_region"; + goto fail_silent; } - return result; -} -status_t CursorWindow::createFromParcel(Parcel* parcel, CursorWindow** outCursorWindow) { - *outCursorWindow = nullptr; + if (ashmem_set_prot_region(ashmemFd, PROT_READ | PROT_WRITE) < 0) { + PLOG(ERROR) << "Failed ashmem_set_prot_region"; + goto fail_silent; + } - String8 name; - status_t result = parcel->readString8(&name); - if (result) return result; + newData = ::mmap(nullptr, mInflatedSize, PROT_READ | PROT_WRITE, MAP_SHARED, ashmemFd, 0); + if (newData == MAP_FAILED) { + PLOG(ERROR) << "Failed mmap"; + goto fail_silent; + } - bool isAshmem; - result = parcel->readBool(&isAshmem); - if (result) return result; + if (ashmem_set_prot_region(ashmemFd, PROT_READ) < 0) { + PLOG(ERROR) << "Failed ashmem_set_prot_region"; + goto fail_silent; + } - if (isAshmem) { - return createFromParcelAshmem(parcel, name, outCursorWindow); - } else { - return createFromParcelInline(parcel, name, outCursorWindow); + { + // Migrate existing contents into new ashmem region + uint32_t slotsSize = mSize - mSlotsOffset; + uint32_t newSlotsOffset = mInflatedSize - slotsSize; + memcpy(static_cast<uint8_t*>(newData), + static_cast<uint8_t*>(mData), mAllocOffset); + memcpy(static_cast<uint8_t*>(newData) + newSlotsOffset, + static_cast<uint8_t*>(mData) + mSlotsOffset, slotsSize); + + free(mData); + mAshmemFd = ashmemFd; + mData = newData; + mSize = mInflatedSize; + mSlotsOffset = newSlotsOffset; + + updateSlotsData(); } + + LOG(DEBUG) << "Inflated: " << this->toString(); + return OK; + +fail: + LOG(ERROR) << "Failed maybeInflate"; +fail_silent: + ::munmap(newData, mInflatedSize); + ::close(ashmemFd); + return UNKNOWN_ERROR; } -status_t CursorWindow::createFromParcelAshmem(Parcel* parcel, String8& name, - CursorWindow** outCursorWindow) { - status_t result; - int actualSize; - int ashmemFd = parcel->readFileDescriptor(); - if (ashmemFd == int(BAD_TYPE)) { - result = BAD_TYPE; - ALOGE("CursorWindow: readFileDescriptor() failed"); +status_t CursorWindow::createFromParcel(Parcel* parcel, CursorWindow** outWindow) { + *outWindow = nullptr; + + CursorWindow* window = new CursorWindow(); + if (!window) goto fail; + + if (parcel->readString8(&window->mName)) goto fail; + if (parcel->readUint32(&window->mNumRows)) goto fail; + if (parcel->readUint32(&window->mNumColumns)) goto fail; + if (parcel->readUint32(&window->mSize)) goto fail; + + if ((window->mNumRows * window->mNumColumns * kSlotSizeBytes) > window->mSize) { + LOG(ERROR) << "Unexpected size " << window->mSize << " for " << window->mNumRows + << " rows and " << window->mNumColumns << " columns"; + goto fail_silent; + } + + bool isAshmem; + if (parcel->readBool(&isAshmem)) goto fail; + if (isAshmem) { + window->mAshmemFd = parcel->readFileDescriptor(); + if (window->mAshmemFd < 0) { + LOG(ERROR) << "Failed readFileDescriptor"; + goto fail_silent; + } + + window->mAshmemFd = ::fcntl(window->mAshmemFd, F_DUPFD_CLOEXEC, 0); + if (window->mAshmemFd < 0) { + PLOG(ERROR) << "Failed F_DUPFD_CLOEXEC"; + goto fail_silent; + } + + window->mData = ::mmap(nullptr, window->mSize, PROT_READ, MAP_SHARED, window->mAshmemFd, 0); + if (window->mData == MAP_FAILED) { + PLOG(ERROR) << "Failed mmap"; + goto fail_silent; + } } else { - ssize_t size = ashmem_get_size_region(ashmemFd); - if (size < 0) { - result = UNKNOWN_ERROR; - ALOGE("CursorWindow: ashmem_get_size_region() failed: errno=%d.", errno); - } else { - int dupAshmemFd = ::fcntl(ashmemFd, F_DUPFD_CLOEXEC, 0); - if (dupAshmemFd < 0) { - result = -errno; - ALOGE("CursorWindow: fcntl() failed: errno=%d.", errno); - } else { - // the size of the ashmem descriptor can be modified between ashmem_get_size_region - // call and mmap, so we'll check again immediately after memory is mapped - void* data = ::mmap(NULL, size, PROT_READ, MAP_SHARED, dupAshmemFd, 0); - if (data == MAP_FAILED) { - result = -errno; - ALOGE("CursorWindow: mmap() failed: errno=%d.", errno); - } else if ((actualSize = ashmem_get_size_region(dupAshmemFd)) != size) { - ::munmap(data, size); - result = BAD_VALUE; - ALOGE("CursorWindow: ashmem_get_size_region() returned %d, expected %d" - " errno=%d", - actualSize, (int) size, errno); - } else { - CursorWindow* window = new CursorWindow(name, dupAshmemFd, - data, size, size, true /*readOnly*/); - LOG_WINDOW("Created CursorWindow from ashmem parcel: freeOffset=%d, " - "numRows=%d, numColumns=%d, mSize=%zu, mData=%p", - window->mHeader->freeOffset, - window->mHeader->numRows, - window->mHeader->numColumns, - window->mSize, window->mData); - *outCursorWindow = window; - return OK; - } - ::close(dupAshmemFd); - } + window->mAshmemFd = -1; + + if (window->mSize > kInlineSize) { + LOG(ERROR) << "Unexpected size " << window->mSize << " for inline window"; + goto fail_silent; } + + window->mData = malloc(window->mSize); + if (!window->mData) goto fail; + + if (parcel->read(window->mData, window->mSize)) goto fail; } - *outCursorWindow = NULL; - return result; -} -status_t CursorWindow::createFromParcelInline(Parcel* parcel, String8& name, - CursorWindow** outCursorWindow) { - uint32_t sentSize; - status_t result = parcel->readUint32(&sentSize); - if (result) return result; - if (sentSize > kInlineSize) return NO_MEMORY; - - void* data = calloc(sentSize, 1); - if (!data) return NO_MEMORY; - - result = parcel->read(data, sentSize); - if (result) return result; - - CursorWindow* window = new CursorWindow(name, -1, data, sentSize, - sentSize, true /*readOnly*/); - LOG_WINDOW("Created CursorWindow from inline parcel: freeOffset=%d, " - "numRows=%d, numColumns=%d, mSize=%zu, mData=%p", - window->mHeader->freeOffset, - window->mHeader->numRows, - window->mHeader->numColumns, - window->mSize, window->mData); - *outCursorWindow = window; + // We just came from a remote source, so we're read-only + // and we can't inflate ourselves + window->mInflatedSize = window->mSize; + window->mReadOnly = true; + + window->updateSlotsData(); + + LOG(DEBUG) << "Created from parcel: " << window->toString(); + *outWindow = window; return OK; + +fail: + LOG(ERROR) << "Failed createFromParcel"; +fail_silent: + delete window; + return UNKNOWN_ERROR; } status_t CursorWindow::writeToParcel(Parcel* parcel) { - LOG_WINDOW("Writing CursorWindow: freeOffset=%d, " - "numRows=%d, numColumns=%d, mSize=%zu, mData=%p", - mHeader->freeOffset, - mHeader->numRows, - mHeader->numColumns, - mSize, mData); - - status_t result = parcel->writeString8(mName); - if (result) return result; + LOG(DEBUG) << "Writing to parcel: " << this->toString(); + if (parcel->writeString8(mName)) goto fail; + if (parcel->writeUint32(mNumRows)) goto fail; + if (parcel->writeUint32(mNumColumns)) goto fail; if (mAshmemFd != -1) { - result = parcel->writeBool(true); - if (result) return result; - return writeToParcelAshmem(parcel); + if (parcel->writeUint32(mSize)) goto fail; + if (parcel->writeBool(true)) goto fail; + if (parcel->writeDupFileDescriptor(mAshmemFd)) goto fail; } else { - result = parcel->writeBool(false); - if (result) return result; - return writeToParcelInline(parcel); + // Since we know we're going to be read-only on the remote side, + // we can compact ourselves on the wire, with just enough padding + // to ensure our slots stay aligned + size_t slotsSize = mSize - mSlotsOffset; + size_t compactedSize = mAllocOffset + slotsSize; + compactedSize = (compactedSize + 3) & ~3; + if (parcel->writeUint32(compactedSize)) goto fail; + if (parcel->writeBool(false)) goto fail; + void* dest = parcel->writeInplace(compactedSize); + if (!dest) goto fail; + memcpy(static_cast<uint8_t*>(dest), + static_cast<uint8_t*>(mData), mAllocOffset); + memcpy(static_cast<uint8_t*>(dest) + compactedSize - slotsSize, + static_cast<uint8_t*>(mData) + mSlotsOffset, slotsSize); } -} - -status_t CursorWindow::writeToParcelAshmem(Parcel* parcel) { - return parcel->writeDupFileDescriptor(mAshmemFd); -} - -status_t CursorWindow::writeToParcelInline(Parcel* parcel) { - status_t result = parcel->writeUint32(mHeader->freeOffset); - if (result) return result; + return OK; - return parcel->write(mData, mHeader->freeOffset); +fail: + LOG(ERROR) << "Failed writeToParcel"; +fail_silent: + return UNKNOWN_ERROR; } status_t CursorWindow::clear() { if (mReadOnly) { return INVALID_OPERATION; } + mAllocOffset = 0; + mSlotsOffset = mSize; + mNumRows = 0; + mNumColumns = 0; + return OK; +} - mHeader->freeOffset = sizeof(Header) + sizeof(RowSlotChunk); - mHeader->firstChunkOffset = sizeof(Header); - mHeader->numRows = 0; - mHeader->numColumns = 0; +void CursorWindow::updateSlotsData() { + mSlotsStart = static_cast<uint8_t*>(mData) + mSize - kSlotSizeBytes; + mSlotsEnd = static_cast<uint8_t*>(mData) + mSlotsOffset; +} - RowSlotChunk* firstChunk = static_cast<RowSlotChunk*>(offsetToPtr(mHeader->firstChunkOffset)); - firstChunk->nextChunkOffset = 0; - return OK; +void* CursorWindow::offsetToPtr(uint32_t offset, uint32_t bufferSize = 0) { + if (offset > mSize) { + LOG(ERROR) << "Offset " << offset + << " out of bounds, max value " << mSize; + return nullptr; + } + if (offset + bufferSize > mSize) { + LOG(ERROR) << "End offset " << (offset + bufferSize) + << " out of bounds, max value " << mSize; + return nullptr; + } + return static_cast<uint8_t*>(mData) + offset; +} + +uint32_t CursorWindow::offsetFromPtr(void* ptr) { + return static_cast<uint8_t*>(ptr) - static_cast<uint8_t*>(mData); } status_t CursorWindow::setNumColumns(uint32_t numColumns) { if (mReadOnly) { return INVALID_OPERATION; } - - uint32_t cur = mHeader->numColumns; - if ((cur > 0 || mHeader->numRows > 0) && cur != numColumns) { - ALOGE("Trying to go from %d columns to %d", cur, numColumns); + uint32_t cur = mNumColumns; + if ((cur > 0 || mNumRows > 0) && cur != numColumns) { + LOG(ERROR) << "Trying to go from " << cur << " columns to " << numColumns; return INVALID_OPERATION; } - mHeader->numColumns = numColumns; + mNumColumns = numColumns; return OK; } @@ -287,30 +290,19 @@ status_t CursorWindow::allocRow() { if (mReadOnly) { return INVALID_OPERATION; } - - // Fill in the row slot - RowSlot* rowSlot = allocRowSlot(); - if (rowSlot == NULL) { - return NO_MEMORY; - } - uint32_t rowSlotOffset = offsetFromPtr(rowSlot); - - // Allocate the slots for the field directory - size_t fieldDirSize = mHeader->numColumns * sizeof(FieldSlot); - uint32_t fieldDirOffset = alloc(fieldDirSize, true /*aligned*/); - if (!fieldDirOffset) { - mHeader->numRows--; - LOG_WINDOW("The row failed, so back out the new row accounting " - "from allocRowSlot %d", mHeader->numRows); - return NO_MEMORY; + size_t size = mNumColumns * kSlotSizeBytes; + int32_t newOffset = mSlotsOffset - size; + if (newOffset < (int32_t) mAllocOffset) { + maybeInflate(); + newOffset = mSlotsOffset - size; + if (newOffset < (int32_t) mAllocOffset) { + return NO_MEMORY; + } } - FieldSlot* fieldDir = static_cast<FieldSlot*>(offsetToPtr(fieldDirOffset)); - memset(fieldDir, 0, fieldDirSize); - - LOG_WINDOW("Allocated row %u, rowSlot is at offset %u, fieldDir is %zu bytes at offset %u\n", - mHeader->numRows - 1, rowSlotOffset, fieldDirSize, fieldDirOffset); - rowSlot = static_cast<RowSlot*>(offsetToPtr(rowSlotOffset)); - rowSlot->offset = fieldDirOffset; + memset(offsetToPtr(newOffset), 0, size); + mSlotsOffset = newOffset; + updateSlotsData(); + mNumRows++; return OK; } @@ -318,90 +310,48 @@ status_t CursorWindow::freeLastRow() { if (mReadOnly) { return INVALID_OPERATION; } - - if (mHeader->numRows > 0) { - mHeader->numRows--; + size_t size = mNumColumns * kSlotSizeBytes; + size_t newOffset = mSlotsOffset + size; + if (newOffset > mSize) { + return NO_MEMORY; } + mSlotsOffset = newOffset; + updateSlotsData(); + mNumRows--; return OK; } -uint32_t CursorWindow::alloc(size_t size, bool aligned) { - uint32_t padding; - if (aligned) { - // 4 byte alignment - padding = (~mHeader->freeOffset + 1) & 3; - } else { - padding = 0; - } - - uint32_t offset = mHeader->freeOffset + padding; - uint32_t nextFreeOffset = offset + size; - if (nextFreeOffset > mSize) { - // Try inflating to ashmem before finally giving up - inflate(); - if (nextFreeOffset > mSize) { - ALOGW("Window is full: requested allocation %zu bytes, " - "free space %zu bytes, window size %zu bytes", - size, freeSpace(), mSize); - return 0; - } - } - - mHeader->freeOffset = nextFreeOffset; - return offset; -} - -CursorWindow::RowSlot* CursorWindow::getRowSlot(uint32_t row) { - uint32_t chunkPos = row; - RowSlotChunk* chunk = static_cast<RowSlotChunk*>( - offsetToPtr(mHeader->firstChunkOffset)); - while (chunkPos >= ROW_SLOT_CHUNK_NUM_ROWS) { - chunk = static_cast<RowSlotChunk*>(offsetToPtr(chunk->nextChunkOffset)); - chunkPos -= ROW_SLOT_CHUNK_NUM_ROWS; - } - return &chunk->slots[chunkPos]; -} - -CursorWindow::RowSlot* CursorWindow::allocRowSlot() { - uint32_t chunkPos = mHeader->numRows; - RowSlotChunk* chunk = static_cast<RowSlotChunk*>( - offsetToPtr(mHeader->firstChunkOffset)); - while (chunkPos > ROW_SLOT_CHUNK_NUM_ROWS) { - chunk = static_cast<RowSlotChunk*>(offsetToPtr(chunk->nextChunkOffset)); - chunkPos -= ROW_SLOT_CHUNK_NUM_ROWS; +status_t CursorWindow::alloc(size_t size, uint32_t* outOffset) { + if (mReadOnly) { + return INVALID_OPERATION; } - if (chunkPos == ROW_SLOT_CHUNK_NUM_ROWS) { - if (!chunk->nextChunkOffset) { - uint32_t chunkOffset = offsetFromPtr(chunk); - uint32_t newChunk = alloc(sizeof(RowSlotChunk), true /*aligned*/); - chunk = static_cast<RowSlotChunk*>(offsetToPtr(chunkOffset)); - chunk->nextChunkOffset = newChunk; - if (!chunk->nextChunkOffset) { - return NULL; - } + size_t alignedSize = (size + 3) & ~3; + size_t newOffset = mAllocOffset + alignedSize; + if (newOffset > mSlotsOffset) { + maybeInflate(); + newOffset = mAllocOffset + alignedSize; + if (newOffset > mSlotsOffset) { + return NO_MEMORY; } - chunk = static_cast<RowSlotChunk*>(offsetToPtr(chunk->nextChunkOffset)); - chunk->nextChunkOffset = 0; - chunkPos = 0; } - mHeader->numRows += 1; - return &chunk->slots[chunkPos]; + *outOffset = mAllocOffset; + mAllocOffset = newOffset; + return OK; } CursorWindow::FieldSlot* CursorWindow::getFieldSlot(uint32_t row, uint32_t column) { - if (row >= mHeader->numRows || column >= mHeader->numColumns) { - ALOGE("Failed to read row %d, column %d from a CursorWindow which " - "has %d rows, %d columns.", - row, column, mHeader->numRows, mHeader->numColumns); - return NULL; - } - RowSlot* rowSlot = getRowSlot(row); - if (!rowSlot) { - ALOGE("Failed to find rowSlot for row %d.", row); - return NULL; + // This is carefully tuned to use as few cycles as + // possible, since this is an extremely hot code path; + // see CursorWindow_bench.cpp for more details + void *result = static_cast<uint8_t*>(mSlotsStart) + - (((row * mNumColumns) + column) << kSlotShift); + if (result < mSlotsEnd || result > mSlotsStart || column >= mNumColumns) { + LOG(ERROR) << "Failed to read row " << row << ", column " << column + << " from a window with " << mNumRows << " rows, " << mNumColumns << " columns"; + return nullptr; + } else { + return static_cast<FieldSlot*>(result); } - FieldSlot* fieldDir = static_cast<FieldSlot*>(offsetToPtr(rowSlot->offset)); - return &fieldDir[column]; } status_t CursorWindow::putBlob(uint32_t row, uint32_t column, const void* value, size_t size) { @@ -423,16 +373,15 @@ status_t CursorWindow::putBlobOrString(uint32_t row, uint32_t column, if (!fieldSlot) { return BAD_VALUE; } - uint32_t fieldSlotOffset = offsetFromPtr(fieldSlot); - uint32_t offset = alloc(size); - if (!offset) { + uint32_t offset; + if (alloc(size, &offset)) { return NO_MEMORY; } memcpy(offsetToPtr(offset), value, size); - fieldSlot = static_cast<FieldSlot*>(offsetToPtr(fieldSlotOffset)); + fieldSlot = getFieldSlot(row, column); fieldSlot->type = type; fieldSlot->data.buffer.offset = offset; fieldSlot->data.buffer.size = size; diff --git a/libs/androidfw/Idmap.cpp b/libs/androidfw/Idmap.cpp index 5f231ffe4786..4e03ce5d9584 100644 --- a/libs/androidfw/Idmap.cpp +++ b/libs/androidfw/Idmap.cpp @@ -36,16 +36,12 @@ using ::android::base::StringPrintf; namespace android { -static bool compare_target_entries(const Idmap_target_entry &e1, const uint32_t target_id) { - return dtohl(e1.target_id) < target_id; -} - -static bool compare_overlay_entries(const Idmap_overlay_entry& e1, const uint32_t overlay_id) { - return dtohl(e1.overlay_id) < overlay_id; +uint32_t round_to_4_bytes(uint32_t size) { + return size + (4U - (size % 4U)) % 4U; } size_t Idmap_header::Size() const { - return sizeof(Idmap_header) + sizeof(uint8_t) * dtohl(debug_info_size); + return sizeof(Idmap_header) + sizeof(uint8_t) * round_to_4_bytes(dtohl(debug_info_size)); } OverlayStringPool::OverlayStringPool(const LoadedIdmap* loaded_idmap) @@ -88,7 +84,10 @@ OverlayDynamicRefTable::OverlayDynamicRefTable(const Idmap_data_header* data_hea status_t OverlayDynamicRefTable::lookupResourceId(uint32_t* resId) const { const Idmap_overlay_entry* first_entry = entries_; const Idmap_overlay_entry* end_entry = entries_ + dtohl(data_header_->overlay_entry_count); - auto entry = std::lower_bound(first_entry, end_entry, *resId, compare_overlay_entries); + auto entry = std::lower_bound(first_entry, end_entry, *resId, + [](const Idmap_overlay_entry& e1, const uint32_t overlay_id) { + return dtohl(e1.overlay_id) < overlay_id; + }); if (entry == end_entry || dtohl(entry->overlay_id) != *resId) { // A mapping for the target resource id could not be found. @@ -96,7 +95,7 @@ status_t OverlayDynamicRefTable::lookupResourceId(uint32_t* resId) const { } *resId = (0x00FFFFFFU & dtohl(entry->target_id)) - | (((uint32_t) target_assigned_package_id_) << 24); + | (((uint32_t) target_assigned_package_id_) << 24U); return NO_ERROR; } @@ -106,62 +105,58 @@ status_t OverlayDynamicRefTable::lookupResourceIdNoRewrite(uint32_t* resId) cons IdmapResMap::IdmapResMap(const Idmap_data_header* data_header, const Idmap_target_entry* entries, + const Idmap_target_entry_inline* inline_entries, uint8_t target_assigned_package_id, const OverlayDynamicRefTable* overlay_ref_table) : data_header_(data_header), entries_(entries), + inline_entries_(inline_entries), target_assigned_package_id_(target_assigned_package_id), - overlay_ref_table_(overlay_ref_table) { }; + overlay_ref_table_(overlay_ref_table) { } IdmapResMap::Result IdmapResMap::Lookup(uint32_t target_res_id) const { - if ((target_res_id >> 24) != target_assigned_package_id_) { + if ((target_res_id >> 24U) != target_assigned_package_id_) { // The resource id must have the same package id as the target package. return {}; } // The resource ids encoded within the idmap are build-time resource ids. target_res_id = (0x00FFFFFFU & target_res_id) - | (((uint32_t) data_header_->target_package_id) << 24); - - const Idmap_target_entry* first_entry = entries_; - const Idmap_target_entry* end_entry = entries_ + dtohl(data_header_->target_entry_count); - auto entry = std::lower_bound(first_entry, end_entry, target_res_id, compare_target_entries); - - if (entry == end_entry || dtohl(entry->target_id) != target_res_id) { - // A mapping for the target resource id could not be found. - return {}; - } - - // A reference should be treated as an alias of the resource. Instead of returning the table - // entry, return the alias resource id to look up. The alias resource might not reside within the - // overlay package, so the resource id must be fixed with the dynamic reference table of the - // overlay before returning. - if (entry->type == Res_value::TYPE_REFERENCE - || entry->type == Res_value::TYPE_DYNAMIC_REFERENCE) { - uint32_t overlay_resource_id = dtohl(entry->value); - + | (((uint32_t) data_header_->target_package_id) << 24U); + + // Check if the target resource is mapped to an overlay resource. + auto first_entry = entries_; + auto end_entry = entries_ + dtohl(data_header_->target_entry_count); + auto entry = std::lower_bound(first_entry, end_entry, target_res_id, + [](const Idmap_target_entry &e, const uint32_t target_id) { + return dtohl(e.target_id) < target_id; + }); + + if (entry != end_entry && dtohl(entry->target_id) == target_res_id) { + uint32_t overlay_resource_id = dtohl(entry->overlay_id); // Lookup the resource without rewriting the overlay resource id back to the target resource id // being looked up. overlay_ref_table_->lookupResourceIdNoRewrite(&overlay_resource_id); return Result(overlay_resource_id); } - // Copy the type and value into the ResTable_entry structure needed by asset manager. - uint16_t malloc_size = sizeof(ResTable_entry) + sizeof(Res_value); - auto table_entry = reinterpret_cast<ResTable_entry*>(malloc(malloc_size)); - memset(table_entry, 0, malloc_size); - table_entry->size = htods(sizeof(ResTable_entry)); - - auto table_value = reinterpret_cast<Res_value*>(reinterpret_cast<uint8_t*>(table_entry) - + sizeof(ResTable_entry)); - table_value->dataType = entry->type; - table_value->data = entry->value; - - return Result(ResTable_entry_handle::managed(table_entry, [](auto p) { free(p); })); + // Check if the target resources is mapped to an inline table entry. + auto first_inline_entry = inline_entries_; + auto end_inline_entry = inline_entries_ + dtohl(data_header_->target_inline_entry_count); + auto inline_entry = std::lower_bound(first_inline_entry, end_inline_entry, target_res_id, + [](const Idmap_target_entry_inline &e, + const uint32_t target_id) { + return dtohl(e.target_id) < target_id; + }); + + if (inline_entry != end_inline_entry && dtohl(inline_entry->target_id) == target_res_id) { + return Result(inline_entry->value); + } + return {}; } static bool is_word_aligned(const void* data) { - return (reinterpret_cast<uintptr_t>(data) & 0x03) == 0; + return (reinterpret_cast<uintptr_t>(data) & 0x03U) == 0U; } static bool IsValidIdmapHeader(const StringPiece& data) { @@ -175,7 +170,7 @@ static bool IsValidIdmapHeader(const StringPiece& data) { return false; } - const Idmap_header* header = reinterpret_cast<const Idmap_header*>(data.data()); + auto header = reinterpret_cast<const Idmap_header*>(data.data()); if (dtohl(header->magic) != kIdmapMagic) { LOG(ERROR) << StringPrintf("Invalid Idmap file: bad magic value (was 0x%08x, expected 0x%08x)", dtohl(header->magic), kIdmapMagic); @@ -198,11 +193,13 @@ LoadedIdmap::LoadedIdmap(std::string&& idmap_path, const Idmap_header* header, const Idmap_data_header* data_header, const Idmap_target_entry* target_entries, + const Idmap_target_entry_inline* target_inline_entries, const Idmap_overlay_entry* overlay_entries, ResStringPool* string_pool) : header_(header), data_header_(data_header), target_entries_(target_entries), + target_inline_entries_(target_inline_entries), overlay_entries_(overlay_entries), string_pool_(string_pool), idmap_path_(std::move(idmap_path)), @@ -233,7 +230,7 @@ std::unique_ptr<const LoadedIdmap> LoadedIdmap::Load(const StringPiece& idmap_pa data_ptr += sizeof(*data_header); data_size -= sizeof(*data_header); - // Make sure there is enough space for the target entries declared in the header. + // Make sure there is enough space for the target entries declared in the header const auto target_entries = reinterpret_cast<const Idmap_target_entry*>(data_ptr); if (data_size / sizeof(Idmap_target_entry) < static_cast<size_t>(dtohl(data_header->target_entry_count))) { @@ -248,6 +245,21 @@ std::unique_ptr<const LoadedIdmap> LoadedIdmap::Load(const StringPiece& idmap_pa data_ptr += target_entry_size_bytes; data_size -= target_entry_size_bytes; + // Make sure there is enough space for the target entries declared in the header. + const auto target_inline_entries = reinterpret_cast<const Idmap_target_entry_inline*>(data_ptr); + if (data_size / sizeof(Idmap_target_entry_inline) < + static_cast<size_t>(dtohl(data_header->target_inline_entry_count))) { + LOG(ERROR) << StringPrintf("Idmap too small for the number of target inline entries (%d)", + (int)dtohl(data_header->target_inline_entry_count)); + return {}; + } + + // Advance the data pointer past the target entries. + const size_t target_inline_entry_size_bytes = + (dtohl(data_header->target_inline_entry_count) * sizeof(Idmap_target_entry_inline)); + data_ptr += target_inline_entry_size_bytes; + data_size -= target_inline_entry_size_bytes; + // Make sure there is enough space for the overlay entries declared in the header. const auto overlay_entries = reinterpret_cast<const Idmap_overlay_entry*>(data_ptr); if (data_size / sizeof(Idmap_overlay_entry) < @@ -257,22 +269,26 @@ std::unique_ptr<const LoadedIdmap> LoadedIdmap::Load(const StringPiece& idmap_pa return {}; } - // Advance the data pointer past the target entries. + // Advance the data pointer past the overlay entries. const size_t overlay_entry_size_bytes = (dtohl(data_header->overlay_entry_count) * sizeof(Idmap_overlay_entry)); data_ptr += overlay_entry_size_bytes; data_size -= overlay_entry_size_bytes; // Read the idmap string pool that holds the value of inline string entries. - if (data_size < dtohl(data_header->string_pool_length)) { + uint32_t string_pool_size = dtohl(*reinterpret_cast<const uint32_t*>(data_ptr)); + data_ptr += sizeof(uint32_t); + data_size -= sizeof(uint32_t); + + if (data_size < string_pool_size) { LOG(ERROR) << StringPrintf("Idmap too small for string pool (length %d)", - (int)dtohl(data_header->string_pool_length)); + (int)string_pool_size); return {}; } auto idmap_string_pool = util::make_unique<ResStringPool>(); - if (dtohl(data_header->string_pool_length) > 0) { - status_t err = idmap_string_pool->setTo(data_ptr, dtohl(data_header->string_pool_length)); + if (string_pool_size > 0) { + status_t err = idmap_string_pool->setTo(data_ptr, string_pool_size); if (err != NO_ERROR) { LOG(ERROR) << "idmap string pool corrupt."; return {}; @@ -280,9 +296,10 @@ std::unique_ptr<const LoadedIdmap> LoadedIdmap::Load(const StringPiece& idmap_pa } // Can't use make_unique because LoadedIdmap constructor is private. - std::unique_ptr<LoadedIdmap> loaded_idmap = std::unique_ptr<LoadedIdmap>( + auto loaded_idmap = std::unique_ptr<LoadedIdmap>( new LoadedIdmap(idmap_path.to_string(), getFileModDate(idmap_path.data()), header, - data_header, target_entries, overlay_entries, idmap_string_pool.release())); + data_header, target_entries, target_inline_entries, overlay_entries, + idmap_string_pool.release())); return std::move(loaded_idmap); } diff --git a/libs/androidfw/fuzz/cursorwindow_fuzzer/Android.bp b/libs/androidfw/fuzz/cursorwindow_fuzzer/Android.bp new file mode 100644 index 000000000000..2dac47b0dac6 --- /dev/null +++ b/libs/androidfw/fuzz/cursorwindow_fuzzer/Android.bp @@ -0,0 +1,31 @@ +cc_fuzz { + name: "cursorwindow_fuzzer", + srcs: [ + "cursorwindow_fuzzer.cpp", + ], + host_supported: true, + corpus: ["corpus/*"], + static_libs: ["libgmock"], + target: { + android: { + shared_libs: [ + "libandroidfw_fuzzer_lib", + "libbase", + "libbinder", + "libcutils", + "liblog", + "libutils", + ], + }, + host: { + static_libs: [ + "libandroidfw_fuzzer_lib", + "libbase", + "libbinder", + "libcutils", + "liblog", + "libutils", + ], + }, + }, +} diff --git a/libs/androidfw/fuzz/cursorwindow_fuzzer/corpus/typical.bin b/libs/androidfw/fuzz/cursorwindow_fuzzer/corpus/typical.bin Binary files differnew file mode 100644 index 000000000000..c7e22dd26ea7 --- /dev/null +++ b/libs/androidfw/fuzz/cursorwindow_fuzzer/corpus/typical.bin diff --git a/libs/androidfw/fuzz/cursorwindow_fuzzer/cursorwindow_fuzzer.cpp b/libs/androidfw/fuzz/cursorwindow_fuzzer/cursorwindow_fuzzer.cpp new file mode 100644 index 000000000000..8dce21220199 --- /dev/null +++ b/libs/androidfw/fuzz/cursorwindow_fuzzer/cursorwindow_fuzzer.cpp @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stddef.h> +#include <stdint.h> +#include <string.h> +#include <string> +#include <memory> + +#include "android-base/logging.h" +#include "androidfw/CursorWindow.h" +#include "binder/Parcel.h" + +#include <fuzzer/FuzzedDataProvider.h> + +using android::CursorWindow; +using android::Parcel; + +extern "C" int LLVMFuzzerInitialize(int *, char ***) { + setenv("ANDROID_LOG_TAGS", "*:s", 1); + android::base::InitLogging(nullptr, &android::base::StderrLogger); + return 0; +} + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + Parcel p; + p.setData(data, size); + + CursorWindow* w = nullptr; + if (!CursorWindow::createFromParcel(&p, &w)) { + LOG(WARNING) << "Valid cursor with " << w->getNumRows() << " rows, " + << w->getNumColumns() << " cols"; + + // Try obtaining heap allocations for most items; we trim the + // search space to speed things up + auto rows = std::min(w->getNumRows(), static_cast<uint32_t>(128)); + auto cols = std::min(w->getNumColumns(), static_cast<uint32_t>(128)); + for (auto row = 0; row < rows; row++) { + for (auto col = 0; col < cols; col++) { + auto field = w->getFieldSlot(row, col); + if (!field) continue; + switch (w->getFieldSlotType(field)) { + case CursorWindow::FIELD_TYPE_STRING: { + size_t size; + w->getFieldSlotValueString(field, &size); + break; + } + case CursorWindow::FIELD_TYPE_BLOB: { + size_t size; + w->getFieldSlotValueBlob(field, &size); + break; + } + } + } + } + + // Finally, try obtaining the furthest valid field + if (rows > 0 && cols > 0) { + w->getFieldSlot(w->getNumRows() - 1, w->getNumColumns() - 1); + } + } + delete w; + + return 0; +} diff --git a/libs/androidfw/include/androidfw/CursorWindow.h b/libs/androidfw/include/androidfw/CursorWindow.h index 0bee60929cc9..6e55a9a0eb8b 100644 --- a/libs/androidfw/include/androidfw/CursorWindow.h +++ b/libs/androidfw/include/androidfw/CursorWindow.h @@ -20,38 +20,36 @@ #include <inttypes.h> #include <stddef.h> #include <stdint.h> +#include <string> -#include <binder/Parcel.h> -#include <log/log.h> -#include <utils/String8.h> +#include "android-base/stringprintf.h" +#include "binder/Parcel.h" +#include "utils/String8.h" -#if LOG_NDEBUG - -#define IF_LOG_WINDOW() if (false) #define LOG_WINDOW(...) -#else - -#define IF_LOG_WINDOW() IF_ALOG(LOG_DEBUG, "CursorWindow") -#define LOG_WINDOW(...) ALOG(LOG_DEBUG, "CursorWindow", __VA_ARGS__) - -#endif - namespace android { /** - * This class stores a set of rows from a database in a buffer. The begining of the - * window has first chunk of RowSlots, which are offsets to the row directory, followed by - * an offset to the next chunk in a linked-list of additional chunk of RowSlots in case - * the pre-allocated chunk isn't big enough to refer to all rows. Each row directory has a - * FieldSlot per column, which has the size, offset, and type of the data for that field. - * Note that the data types come from sqlite3.h. + * This class stores a set of rows from a database in a buffer. Internally + * data is structured as a "heap" of string/blob allocations at the bottom + * of the memory region, and a "stack" of FieldSlot allocations at the top + * of the memory region. Here's an example visual representation: + * + * +----------------------------------------------------------------+ + * |heap\0of\0strings\0 222211110000| ... + * +-------------------+--------------------------------+-------+---+ + * ^ ^ ^ ^ ^ ^ + * | | | | | | + * | +- mAllocOffset mSlotsOffset -+ | | | + * +- mData mSlotsStart -+ | | + * mSize -+ | + * mInflatedSize -+ * * Strings are stored in UTF-8. */ class CursorWindow { - CursorWindow(const String8& name, int ashmemFd, void* data, size_t size, - size_t inflatedSize, bool readOnly); + CursorWindow(); public: /* Field types. */ @@ -88,9 +86,9 @@ public: inline String8 name() { return mName; } inline size_t size() { return mSize; } - inline size_t freeSpace() { return mSize - mHeader->freeOffset; } - inline uint32_t getNumRows() { return mHeader->numRows; } - inline uint32_t getNumColumns() { return mHeader->numColumns; } + inline size_t freeSpace() { return mSlotsOffset - mAllocOffset; } + inline uint32_t getNumRows() { return mNumRows; } + inline uint32_t getNumColumns() { return mNumColumns; } status_t clear(); status_t setNumColumns(uint32_t numColumns); @@ -138,75 +136,57 @@ public: return offsetToPtr(fieldSlot->data.buffer.offset, fieldSlot->data.buffer.size); } -private: - static const size_t ROW_SLOT_CHUNK_NUM_ROWS = 100; - - struct Header { - // Offset of the lowest unused byte in the window. - uint32_t freeOffset; - - // Offset of the first row slot chunk. - uint32_t firstChunkOffset; - - uint32_t numRows; - uint32_t numColumns; - }; - - struct RowSlot { - uint32_t offset; - }; - - struct RowSlotChunk { - RowSlot slots[ROW_SLOT_CHUNK_NUM_ROWS]; - uint32_t nextChunkOffset; - }; - - String8 mName; - int mAshmemFd; - void* mData; - size_t mSize; - size_t mInflatedSize; - bool mReadOnly; - Header* mHeader; - - inline void* offsetToPtr(uint32_t offset, uint32_t bufferSize = 0) { - if (offset > mSize) { - ALOGE("Offset %" PRIu32 " out of bounds, max value %zu", offset, mSize); - return NULL; - } - if (offset + bufferSize > mSize) { - ALOGE("End offset %" PRIu32 " out of bounds, max value %zu", - offset + bufferSize, mSize); - return NULL; - } - return static_cast<uint8_t*>(mData) + offset; + inline std::string toString() const { + return android::base::StringPrintf("CursorWindow{name=%s, fd=%d, size=%d, inflatedSize=%d, " + "allocOffset=%d, slotsOffset=%d, numRows=%d, numColumns=%d}", mName.c_str(), + mAshmemFd, mSize, mInflatedSize, mAllocOffset, mSlotsOffset, mNumRows, mNumColumns); } - inline uint32_t offsetFromPtr(void* ptr) { - return static_cast<uint8_t*>(ptr) - static_cast<uint8_t*>(mData); - } +private: + String8 mName; + int mAshmemFd = -1; + void* mData = nullptr; + /** + * Pointer to the first FieldSlot, used to optimize the extremely + * hot code path of getFieldSlot(). + */ + void* mSlotsStart = nullptr; + void* mSlotsEnd = nullptr; + uint32_t mSize = 0; + /** + * When a window starts as lightweight inline allocation, this value + * holds the "full" size to be created after ashmem inflation. + */ + uint32_t mInflatedSize = 0; + /** + * Offset to the top of the "heap" of string/blob allocations. By + * storing these allocations at the bottom of our memory region we + * avoid having to rewrite offsets when inflating. + */ + uint32_t mAllocOffset = 0; + /** + * Offset to the bottom of the "stack" of FieldSlot allocations. + */ + uint32_t mSlotsOffset = 0; + uint32_t mNumRows = 0; + uint32_t mNumColumns = 0; + bool mReadOnly = false; - static status_t createFromParcelAshmem(Parcel*, String8&, CursorWindow**); - static status_t createFromParcelInline(Parcel*, String8&, CursorWindow**); + void updateSlotsData(); - status_t writeToParcelAshmem(Parcel*); - status_t writeToParcelInline(Parcel*); + void* offsetToPtr(uint32_t offset, uint32_t bufferSize); + uint32_t offsetFromPtr(void* ptr); /** * By default windows are lightweight inline allocations; this method * inflates the window into a larger ashmem region. */ - status_t inflate(); + status_t maybeInflate(); /** - * Allocate a portion of the window. Returns the offset - * of the allocation, or 0 if there isn't enough space. - * If aligned is true, the allocation gets 4 byte alignment. + * Allocate a portion of the window. */ - uint32_t alloc(size_t size, bool aligned = false); - - RowSlot* getRowSlot(uint32_t row); - RowSlot* allocRowSlot(); + status_t alloc(size_t size, uint32_t* outOffset); status_t putBlobOrString(uint32_t row, uint32_t column, const void* value, size_t size, int32_t type); diff --git a/libs/androidfw/include/androidfw/Idmap.h b/libs/androidfw/include/androidfw/Idmap.h index ecc1ce65d124..ab0f47f025d2 100644 --- a/libs/androidfw/include/androidfw/Idmap.h +++ b/libs/androidfw/include/androidfw/Idmap.h @@ -77,40 +77,40 @@ class OverlayDynamicRefTable : public DynamicRefTable { // A mapping of target resource ids to a values or resource ids that should overlay the target. class IdmapResMap { public: - // Represents the result of a idmap lookup. The result can be one of three possibillities: + // Represents the result of a idmap lookup. The result can be one of three possibilities: // 1) The result is a resource id which represents the overlay resource that should act as an // alias of the target resource. // 2) The result is a table entry which overlays the type and value of the target resource. // 3) The result is neither and the target resource is not overlaid. class Result { public: - Result() : data_(nullptr) {}; + Result() = default; explicit Result(uint32_t value) : data_(value) {}; - explicit Result(ResTable_entry_handle&& value) : data_(value) { }; + explicit Result(const Res_value& value) : data_(value) { }; // Returns `true` if the resource is overlaid. - inline explicit operator bool() const { - return !std::get_if<nullptr_t>(&data_); + explicit operator bool() const { + return std::get_if<std::monostate>(&data_) == nullptr; } - inline bool IsResourceId() const { - return std::get_if<uint32_t>(&data_); + bool IsResourceId() const { + return std::get_if<uint32_t>(&data_) != nullptr; } - inline uint32_t GetResourceId() const { - return *std::get_if<uint32_t>(&data_); + uint32_t GetResourceId() const { + return std::get<uint32_t>(data_); } - inline bool IsTableEntry() const { - return std::get_if<ResTable_entry_handle>(&data_); + bool IsInlineValue() const { + return std::get_if<Res_value>(&data_) != nullptr; } - inline const ResTable_entry_handle& GetTableEntry() const { - return *std::get_if<ResTable_entry_handle>(&data_); + const Res_value& GetInlineValue() const { + return std::get<Res_value>(data_); } private: - std::variant<uint32_t, nullptr_t, ResTable_entry_handle> data_; + std::variant<std::monostate, uint32_t, Res_value> data_; }; // Looks up the value that overlays the target resource id. @@ -123,11 +123,13 @@ class IdmapResMap { private: explicit IdmapResMap(const Idmap_data_header* data_header, const Idmap_target_entry* entries, + const Idmap_target_entry_inline* inline_entries, uint8_t target_assigned_package_id, const OverlayDynamicRefTable* overlay_ref_table); const Idmap_data_header* data_header_; const Idmap_target_entry* entries_; + const Idmap_target_entry_inline* inline_entries_; const uint8_t target_assigned_package_id_; const OverlayDynamicRefTable* overlay_ref_table_; @@ -163,8 +165,8 @@ class LoadedIdmap { // Returns a mapping from target resource ids to overlay values. inline const IdmapResMap GetTargetResourcesMap( uint8_t target_assigned_package_id, const OverlayDynamicRefTable* overlay_ref_table) const { - return IdmapResMap(data_header_, target_entries_, target_assigned_package_id, - overlay_ref_table); + return IdmapResMap(data_header_, target_entries_, target_inline_entries_, + target_assigned_package_id, overlay_ref_table); } // Returns a dynamic reference table for a loaded overlay package. @@ -184,6 +186,7 @@ class LoadedIdmap { const Idmap_header* header_; const Idmap_data_header* data_header_; const Idmap_target_entry* target_entries_; + const Idmap_target_entry_inline* target_inline_entries_; const Idmap_overlay_entry* overlay_entries_; const std::unique_ptr<ResStringPool> string_pool_; @@ -200,6 +203,7 @@ class LoadedIdmap { const Idmap_header* header, const Idmap_data_header* data_header, const Idmap_target_entry* target_entries, + const Idmap_target_entry_inline* target_inline_entries, const Idmap_overlay_entry* overlay_entries, ResStringPool* string_pool); diff --git a/libs/androidfw/include/androidfw/ResourceTypes.h b/libs/androidfw/include/androidfw/ResourceTypes.h index e10a7f3f5c61..04ba78b6705d 100644 --- a/libs/androidfw/include/androidfw/ResourceTypes.h +++ b/libs/androidfw/include/androidfw/ResourceTypes.h @@ -41,7 +41,7 @@ namespace android { constexpr const static uint32_t kIdmapMagic = 0x504D4449u; -constexpr const static uint32_t kIdmapCurrentVersion = 0x00000004u; +constexpr const static uint32_t kIdmapCurrentVersion = 0x00000005u; /** * In C++11, char16_t is defined as *at least* 16 bits. We do a lot of @@ -1476,7 +1476,7 @@ struct ResTable_entry // If set, this is a weak resource and may be overriden by strong // resources of the same name/type. This is only useful during // linking with other resource tables. - FLAG_WEAK = 0x0004 + FLAG_WEAK = 0x0004, }; uint16_t flags; @@ -1586,50 +1586,6 @@ struct ResTable_map Res_value value; }; - -// A ResTable_entry variant that either holds an unmanaged pointer to a constant ResTable_entry or -// holds a ResTable_entry which is tied to the lifetime of the handle. -class ResTable_entry_handle { - public: - ResTable_entry_handle() = default; - - ResTable_entry_handle(const ResTable_entry_handle& handle) { - entry_ = handle.entry_; - } - - ResTable_entry_handle(ResTable_entry_handle&& handle) noexcept { - entry_ = handle.entry_; - } - - inline static ResTable_entry_handle managed(ResTable_entry* entry, void (*deleter)(void *)) { - return ResTable_entry_handle(std::shared_ptr<const ResTable_entry>(entry, deleter)); - } - - inline static ResTable_entry_handle unmanaged(const ResTable_entry* entry) { - return ResTable_entry_handle(std::shared_ptr<const ResTable_entry>(entry, [](auto /*p */){})); - } - - inline ResTable_entry_handle& operator=(const ResTable_entry_handle& handle) noexcept { - entry_ = handle.entry_; - return *this; - } - - inline ResTable_entry_handle& operator=(ResTable_entry_handle&& handle) noexcept { - entry_ = handle.entry_; - return *this; - } - - inline const ResTable_entry* operator*() & { - return entry_.get(); - } - - private: - explicit ResTable_entry_handle(std::shared_ptr<const ResTable_entry> entry) - : entry_(std::move(entry)) { } - - std::shared_ptr<const ResTable_entry> entry_; -}; - /** * A package-id to package name mapping for any shared libraries used * in this resource table. The package-id's encoded in this resource @@ -1740,7 +1696,6 @@ inline ResTable_overlayable_policy_header::PolicyFlags& operator |=( return first; } -#pragma pack(push, 1) struct Idmap_header { // Always 0x504D4449 ('IDMP') uint32_t magic; @@ -1751,7 +1706,7 @@ struct Idmap_header { uint32_t overlay_crc32; uint32_t fulfilled_policies; - uint8_t enforce_overlayable; + uint32_t enforce_overlayable; uint8_t target_path[256]; uint8_t overlay_path[256]; @@ -1765,23 +1720,31 @@ struct Idmap_header { struct Idmap_data_header { uint8_t target_package_id; uint8_t overlay_package_id; + + // Padding to ensure 4 byte alignment for target_entry_count + uint16_t p0; + uint32_t target_entry_count; + uint32_t target_inline_entry_count; uint32_t overlay_entry_count; + uint32_t string_pool_index_offset; - uint32_t string_pool_length; }; struct Idmap_target_entry { uint32_t target_id; - uint8_t type; - uint32_t value; + uint32_t overlay_id; +}; + +struct Idmap_target_entry_inline { + uint32_t target_id; + Res_value value; }; struct Idmap_overlay_entry { uint32_t overlay_id; uint32_t target_id; }; -#pragma pack(pop) class AssetManager2; diff --git a/libs/androidfw/tests/CursorWindow_bench.cpp b/libs/androidfw/tests/CursorWindow_bench.cpp new file mode 100644 index 000000000000..f1191c3d7213 --- /dev/null +++ b/libs/androidfw/tests/CursorWindow_bench.cpp @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "benchmark/benchmark.h" + +#include "androidfw/CursorWindow.h" + +namespace android { + +static void BM_CursorWindowWrite(benchmark::State& state, size_t rows, size_t cols) { + CursorWindow* w; + CursorWindow::create(String8("test"), 1 << 21, &w); + + while (state.KeepRunning()) { + w->clear(); + w->setNumColumns(cols); + for (int row = 0; row < rows; row++) { + w->allocRow(); + for (int col = 0; col < cols; col++) { + w->putLong(row, col, 0xcafe); + } + } + } +} + +static void BM_CursorWindowWrite4x4(benchmark::State& state) { + BM_CursorWindowWrite(state, 4, 4); +} +BENCHMARK(BM_CursorWindowWrite4x4); + +static void BM_CursorWindowWrite1Kx4(benchmark::State& state) { + BM_CursorWindowWrite(state, 1024, 4); +} +BENCHMARK(BM_CursorWindowWrite1Kx4); + +static void BM_CursorWindowWrite16Kx4(benchmark::State& state) { + BM_CursorWindowWrite(state, 16384, 4); +} +BENCHMARK(BM_CursorWindowWrite16Kx4); + +static void BM_CursorWindowRead(benchmark::State& state, size_t rows, size_t cols) { + CursorWindow* w; + CursorWindow::create(String8("test"), 1 << 21, &w); + w->setNumColumns(cols); + for (int row = 0; row < rows; row++) { + w->allocRow(); + } + + while (state.KeepRunning()) { + for (int row = 0; row < rows; row++) { + for (int col = 0; col < cols; col++) { + w->getFieldSlot(row, col); + } + } + } +} + +static void BM_CursorWindowRead4x4(benchmark::State& state) { + BM_CursorWindowRead(state, 4, 4); +} +BENCHMARK(BM_CursorWindowRead4x4); + +static void BM_CursorWindowRead1Kx4(benchmark::State& state) { + BM_CursorWindowRead(state, 1024, 4); +} +BENCHMARK(BM_CursorWindowRead1Kx4); + +static void BM_CursorWindowRead16Kx4(benchmark::State& state) { + BM_CursorWindowRead(state, 16384, 4); +} +BENCHMARK(BM_CursorWindowRead16Kx4); + +} // namespace android diff --git a/libs/androidfw/tests/CursorWindow_test.cpp b/libs/androidfw/tests/CursorWindow_test.cpp new file mode 100644 index 000000000000..15be80c48192 --- /dev/null +++ b/libs/androidfw/tests/CursorWindow_test.cpp @@ -0,0 +1,367 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <utility> + +#include "androidfw/CursorWindow.h" + +#include "TestHelpers.h" + +#define CREATE_WINDOW_1K \ + CursorWindow* w; \ + CursorWindow::create(String8("test"), 1 << 10, &w); + +#define CREATE_WINDOW_1K_3X3 \ + CursorWindow* w; \ + CursorWindow::create(String8("test"), 1 << 10, &w); \ + ASSERT_EQ(w->setNumColumns(3), OK); \ + ASSERT_EQ(w->allocRow(), OK); \ + ASSERT_EQ(w->allocRow(), OK); \ + ASSERT_EQ(w->allocRow(), OK); + +#define CREATE_WINDOW_2M \ + CursorWindow* w; \ + CursorWindow::create(String8("test"), 1 << 21, &w); + +static constexpr const size_t kHalfInlineSize = 8192; +static constexpr const size_t kGiantSize = 1048576; + +namespace android { + +TEST(CursorWindowTest, Empty) { + CREATE_WINDOW_1K; + + ASSERT_EQ(w->getNumRows(), 0); + ASSERT_EQ(w->getNumColumns(), 0); + ASSERT_EQ(w->size(), 1 << 10); + ASSERT_EQ(w->freeSpace(), 1 << 10); +} + +TEST(CursorWindowTest, SetNumColumns) { + CREATE_WINDOW_1K; + + // Once we've locked in columns, we can't adjust + ASSERT_EQ(w->getNumColumns(), 0); + ASSERT_EQ(w->setNumColumns(4), OK); + ASSERT_NE(w->setNumColumns(5), OK); + ASSERT_NE(w->setNumColumns(3), OK); + ASSERT_EQ(w->getNumColumns(), 4); +} + +TEST(CursorWindowTest, SetNumColumnsAfterRow) { + CREATE_WINDOW_1K; + + // Once we've locked in a row, we can't adjust columns + ASSERT_EQ(w->getNumColumns(), 0); + ASSERT_EQ(w->allocRow(), OK); + ASSERT_NE(w->setNumColumns(4), OK); + ASSERT_EQ(w->getNumColumns(), 0); +} + +TEST(CursorWindowTest, AllocRow) { + CREATE_WINDOW_1K; + + ASSERT_EQ(w->setNumColumns(4), OK); + + // Rolling forward means we have less free space + ASSERT_EQ(w->getNumRows(), 0); + auto before = w->freeSpace(); + ASSERT_EQ(w->allocRow(), OK); + ASSERT_LT(w->freeSpace(), before); + ASSERT_EQ(w->getNumRows(), 1); + + // Verify we can unwind + ASSERT_EQ(w->freeLastRow(), OK); + ASSERT_EQ(w->freeSpace(), before); + ASSERT_EQ(w->getNumRows(), 0); + + // Can't unwind when no rows left + ASSERT_NE(w->freeLastRow(), OK); +} + +TEST(CursorWindowTest, AllocRowBounds) { + CREATE_WINDOW_1K; + + // 60 columns is 960 bytes, which means only a single row can fit + ASSERT_EQ(w->setNumColumns(60), OK); + ASSERT_EQ(w->allocRow(), OK); + ASSERT_NE(w->allocRow(), OK); +} + +TEST(CursorWindowTest, StoreNull) { + CREATE_WINDOW_1K_3X3; + + ASSERT_EQ(w->putNull(1, 1), OK); + ASSERT_EQ(w->putNull(0, 0), OK); + + { + auto field = w->getFieldSlot(1, 1); + ASSERT_EQ(w->getFieldSlotType(field), CursorWindow::FIELD_TYPE_NULL); + } + { + auto field = w->getFieldSlot(0, 0); + ASSERT_EQ(w->getFieldSlotType(field), CursorWindow::FIELD_TYPE_NULL); + } +} + +TEST(CursorWindowTest, StoreLong) { + CREATE_WINDOW_1K_3X3; + + ASSERT_EQ(w->putLong(1, 1, 0xf00d), OK); + ASSERT_EQ(w->putLong(0, 0, 0xcafe), OK); + + { + auto field = w->getFieldSlot(1, 1); + ASSERT_EQ(w->getFieldSlotType(field), CursorWindow::FIELD_TYPE_INTEGER); + ASSERT_EQ(w->getFieldSlotValueLong(field), 0xf00d); + } + { + auto field = w->getFieldSlot(0, 0); + ASSERT_EQ(w->getFieldSlotType(field), CursorWindow::FIELD_TYPE_INTEGER); + ASSERT_EQ(w->getFieldSlotValueLong(field), 0xcafe); + } +} + +TEST(CursorWindowTest, StoreString) { + CREATE_WINDOW_1K_3X3; + + ASSERT_EQ(w->putString(1, 1, "food", 5), OK); + ASSERT_EQ(w->putString(0, 0, "cafe", 5), OK); + + size_t size; + { + auto field = w->getFieldSlot(1, 1); + ASSERT_EQ(w->getFieldSlotType(field), CursorWindow::FIELD_TYPE_STRING); + auto actual = w->getFieldSlotValueString(field, &size); + ASSERT_EQ(std::string(actual), "food"); + } + { + auto field = w->getFieldSlot(0, 0); + ASSERT_EQ(w->getFieldSlotType(field), CursorWindow::FIELD_TYPE_STRING); + auto actual = w->getFieldSlotValueString(field, &size); + ASSERT_EQ(std::string(actual), "cafe"); + } +} + +TEST(CursorWindowTest, StoreBounds) { + CREATE_WINDOW_1K_3X3; + + // Can't work with values beyond bounds + ASSERT_NE(w->putLong(0, 3, 0xcafe), OK); + ASSERT_NE(w->putLong(3, 0, 0xcafe), OK); + ASSERT_NE(w->putLong(3, 3, 0xcafe), OK); + ASSERT_EQ(w->getFieldSlot(0, 3), nullptr); + ASSERT_EQ(w->getFieldSlot(3, 0), nullptr); + ASSERT_EQ(w->getFieldSlot(3, 3), nullptr); + + // Can't work with invalid indexes + ASSERT_NE(w->putLong(-1, 0, 0xcafe), OK); + ASSERT_NE(w->putLong(0, -1, 0xcafe), OK); + ASSERT_NE(w->putLong(-1, -1, 0xcafe), OK); + ASSERT_EQ(w->getFieldSlot(-1, 0), nullptr); + ASSERT_EQ(w->getFieldSlot(0, -1), nullptr); + ASSERT_EQ(w->getFieldSlot(-1, -1), nullptr); +} + +TEST(CursorWindowTest, Inflate) { + CREATE_WINDOW_2M; + + auto before = w->size(); + ASSERT_EQ(w->setNumColumns(4), OK); + ASSERT_EQ(w->allocRow(), OK); + + // Scratch buffer that will fit before inflation + void* buf = malloc(kHalfInlineSize); + + // Store simple value + ASSERT_EQ(w->putLong(0, 0, 0xcafe), OK); + + // Store first object that fits inside + memset(buf, 42, kHalfInlineSize); + ASSERT_EQ(w->putBlob(0, 1, buf, kHalfInlineSize), OK); + ASSERT_EQ(w->size(), before); + + // Store second simple value + ASSERT_EQ(w->putLong(0, 2, 0xface), OK); + + // Store second object that requires inflation + memset(buf, 84, kHalfInlineSize); + ASSERT_EQ(w->putBlob(0, 3, buf, kHalfInlineSize), OK); + ASSERT_GT(w->size(), before); + + // Verify data is intact + { + auto field = w->getFieldSlot(0, 0); + ASSERT_EQ(w->getFieldSlotType(field), CursorWindow::FIELD_TYPE_INTEGER); + ASSERT_EQ(w->getFieldSlotValueLong(field), 0xcafe); + } + { + auto field = w->getFieldSlot(0, 1); + ASSERT_EQ(w->getFieldSlotType(field), CursorWindow::FIELD_TYPE_BLOB); + size_t actualSize; + auto actual = w->getFieldSlotValueBlob(field, &actualSize); + ASSERT_EQ(actualSize, kHalfInlineSize); + memset(buf, 42, kHalfInlineSize); + ASSERT_NE(actual, buf); + ASSERT_EQ(memcmp(buf, actual, kHalfInlineSize), 0); + } + { + auto field = w->getFieldSlot(0, 2); + ASSERT_EQ(w->getFieldSlotType(field), CursorWindow::FIELD_TYPE_INTEGER); + ASSERT_EQ(w->getFieldSlotValueLong(field), 0xface); + } + { + auto field = w->getFieldSlot(0, 3); + ASSERT_EQ(w->getFieldSlotType(field), CursorWindow::FIELD_TYPE_BLOB); + size_t actualSize; + auto actual = w->getFieldSlotValueBlob(field, &actualSize); + ASSERT_EQ(actualSize, kHalfInlineSize); + memset(buf, 84, kHalfInlineSize); + ASSERT_NE(actual, buf); + ASSERT_EQ(memcmp(buf, actual, kHalfInlineSize), 0); + } +} + +TEST(CursorWindowTest, ParcelEmpty) { + CREATE_WINDOW_2M; + + Parcel p; + w->writeToParcel(&p); + p.setDataPosition(0); + w = nullptr; + + ASSERT_EQ(CursorWindow::createFromParcel(&p, &w), OK); + ASSERT_EQ(w->getNumRows(), 0); + ASSERT_EQ(w->getNumColumns(), 0); + ASSERT_EQ(w->size(), 0); + ASSERT_EQ(w->freeSpace(), 0); + + // We can't mutate the window after parceling + ASSERT_NE(w->setNumColumns(4), OK); + ASSERT_NE(w->allocRow(), OK); +} + +TEST(CursorWindowTest, ParcelSmall) { + CREATE_WINDOW_2M; + + auto before = w->size(); + ASSERT_EQ(w->setNumColumns(4), OK); + ASSERT_EQ(w->allocRow(), OK); + + // Scratch buffer that will fit before inflation + void* buf = malloc(kHalfInlineSize); + + // Store simple value + ASSERT_EQ(w->putLong(0, 0, 0xcafe), OK); + + // Store first object that fits inside + memset(buf, 42, kHalfInlineSize); + ASSERT_EQ(w->putBlob(0, 1, buf, kHalfInlineSize), OK); + ASSERT_EQ(w->size(), before); + + // Store second object with zero length + ASSERT_EQ(w->putBlob(0, 2, buf, 0), OK); + ASSERT_EQ(w->size(), before); + + // Force through a parcel + Parcel p; + w->writeToParcel(&p); + p.setDataPosition(0); + w = nullptr; + + ASSERT_EQ(CursorWindow::createFromParcel(&p, &w), OK); + ASSERT_EQ(w->getNumRows(), 1); + ASSERT_EQ(w->getNumColumns(), 4); + + // Verify data is intact + { + auto field = w->getFieldSlot(0, 0); + ASSERT_EQ(w->getFieldSlotType(field), CursorWindow::FIELD_TYPE_INTEGER); + ASSERT_EQ(w->getFieldSlotValueLong(field), 0xcafe); + } + { + auto field = w->getFieldSlot(0, 1); + ASSERT_EQ(w->getFieldSlotType(field), CursorWindow::FIELD_TYPE_BLOB); + size_t actualSize; + auto actual = w->getFieldSlotValueBlob(field, &actualSize); + ASSERT_EQ(actualSize, kHalfInlineSize); + memset(buf, 42, kHalfInlineSize); + ASSERT_NE(actual, buf); + ASSERT_EQ(memcmp(buf, actual, kHalfInlineSize), 0); + } + { + auto field = w->getFieldSlot(0, 2); + ASSERT_EQ(w->getFieldSlotType(field), CursorWindow::FIELD_TYPE_BLOB); + size_t actualSize; + auto actual = w->getFieldSlotValueBlob(field, &actualSize); + ASSERT_EQ(actualSize, 0); + ASSERT_NE(actual, nullptr); + } +} + +TEST(CursorWindowTest, ParcelLarge) { + CREATE_WINDOW_2M; + + ASSERT_EQ(w->setNumColumns(4), OK); + ASSERT_EQ(w->allocRow(), OK); + + // Store simple value + ASSERT_EQ(w->putLong(0, 0, 0xcafe), OK); + + // Store object that forces inflation + void* buf = malloc(kGiantSize); + memset(buf, 42, kGiantSize); + ASSERT_EQ(w->putBlob(0, 1, buf, kGiantSize), OK); + + // Store second object with zero length + ASSERT_EQ(w->putBlob(0, 2, buf, 0), OK); + + // Force through a parcel + Parcel p; + w->writeToParcel(&p); + p.setDataPosition(0); + w = nullptr; + + ASSERT_EQ(CursorWindow::createFromParcel(&p, &w), OK); + ASSERT_EQ(w->getNumRows(), 1); + ASSERT_EQ(w->getNumColumns(), 4); + + // Verify data is intact + { + auto field = w->getFieldSlot(0, 0); + ASSERT_EQ(w->getFieldSlotType(field), CursorWindow::FIELD_TYPE_INTEGER); + ASSERT_EQ(w->getFieldSlotValueLong(field), 0xcafe); + } + { + auto field = w->getFieldSlot(0, 1); + ASSERT_EQ(w->getFieldSlotType(field), CursorWindow::FIELD_TYPE_BLOB); + size_t actualSize; + auto actual = w->getFieldSlotValueBlob(field, &actualSize); + ASSERT_EQ(actualSize, kGiantSize); + memset(buf, 42, kGiantSize); + ASSERT_EQ(memcmp(buf, actual, kGiantSize), 0); + } + { + auto field = w->getFieldSlot(0, 2); + ASSERT_EQ(w->getFieldSlotType(field), CursorWindow::FIELD_TYPE_BLOB); + size_t actualSize; + auto actual = w->getFieldSlotValueBlob(field, &actualSize); + ASSERT_EQ(actualSize, 0); + ASSERT_NE(actual, nullptr); + } +} + +} // android diff --git a/libs/androidfw/tests/data/overlay/overlay.apk b/libs/androidfw/tests/data/overlay/overlay.apk Binary files differindex f1ed59279fdb..c9bf2527c82a 100644 --- a/libs/androidfw/tests/data/overlay/overlay.apk +++ b/libs/androidfw/tests/data/overlay/overlay.apk diff --git a/libs/androidfw/tests/data/overlay/overlay.idmap b/libs/androidfw/tests/data/overlay/overlay.idmap Binary files differindex 29c5eb6a9ccf..3ab244eb084a 100644 --- a/libs/androidfw/tests/data/overlay/overlay.idmap +++ b/libs/androidfw/tests/data/overlay/overlay.idmap diff --git a/libs/hwui/FrameInfo.h b/libs/hwui/FrameInfo.h index d24eca7d00c6..bb875e35f6f7 100644 --- a/libs/hwui/FrameInfo.h +++ b/libs/hwui/FrameInfo.h @@ -158,7 +158,7 @@ public: // GPU start time is approximated to the moment before swapBuffer is invoked. // We could add an EGLSyncKHR fence at the beginning of the frame, but that is an overhead. int64_t endTime = get(FrameInfoIndex::GpuCompleted); - return endTime > 0 ? endTime - get(FrameInfoIndex::SwapBuffers) : -1; + return endTime > 0 ? endTime - get(FrameInfoIndex::SwapBuffers) : 0; } inline int64_t& set(FrameInfoIndex index) { return mFrameInfo[static_cast<int>(index)]; } diff --git a/libs/hwui/hwui/MinikinUtils.cpp b/libs/hwui/hwui/MinikinUtils.cpp index 5f6b53ac767f..b8029087cb4f 100644 --- a/libs/hwui/hwui/MinikinUtils.cpp +++ b/libs/hwui/hwui/MinikinUtils.cpp @@ -21,6 +21,7 @@ #include <log/log.h> #include <minikin/MeasuredText.h> +#include <minikin/Measurement.h> #include "Paint.h" #include "SkPathMeasure.h" #include "Typeface.h" @@ -69,6 +70,18 @@ minikin::Layout MinikinUtils::doLayout(const Paint* paint, minikin::Bidi bidiFla } } +void MinikinUtils::getBounds(const Paint* paint, minikin::Bidi bidiFlags, const Typeface* typeface, + const uint16_t* buf, size_t bufSize, minikin::MinikinRect* out) { + minikin::MinikinPaint minikinPaint = prepareMinikinPaint(paint, typeface); + + const minikin::U16StringPiece textBuf(buf, bufSize); + const minikin::StartHyphenEdit startHyphen = paint->getStartHyphenEdit(); + const minikin::EndHyphenEdit endHyphen = paint->getEndHyphenEdit(); + + minikin::getBounds(textBuf, minikin::Range(0, textBuf.size()), bidiFlags, minikinPaint, + startHyphen, endHyphen, out); +} + float MinikinUtils::measureText(const Paint* paint, minikin::Bidi bidiFlags, const Typeface* typeface, const uint16_t* buf, size_t start, size_t count, size_t bufSize, float* advances) { diff --git a/libs/hwui/hwui/MinikinUtils.h b/libs/hwui/hwui/MinikinUtils.h index 7c3f0d84a75b..a15803ad2dca 100644 --- a/libs/hwui/hwui/MinikinUtils.h +++ b/libs/hwui/hwui/MinikinUtils.h @@ -48,6 +48,9 @@ public: size_t contextStart, size_t contextCount, minikin::MeasuredText* mt); + static void getBounds(const Paint* paint, minikin::Bidi bidiFlags, const Typeface* typeface, + const uint16_t* buf, size_t bufSize, minikin::MinikinRect* out); + static float measureText(const Paint* paint, minikin::Bidi bidiFlags, const Typeface* typeface, const uint16_t* buf, size_t start, size_t count, size_t bufSize, diff --git a/libs/hwui/jni/Paint.cpp b/libs/hwui/jni/Paint.cpp index 89ff9b257642..3c86b28262b0 100644 --- a/libs/hwui/jni/Paint.cpp +++ b/libs/hwui/jni/Paint.cpp @@ -339,18 +339,13 @@ namespace PaintGlue { } static void doTextBounds(JNIEnv* env, const jchar* text, int count, jobject bounds, - const Paint& paint, const Typeface* typeface, jint bidiFlags) { + const Paint& paint, const Typeface* typeface, jint bidiFlagsInt) { SkRect r; SkIRect ir; - minikin::Layout layout = MinikinUtils::doLayout(&paint, - static_cast<minikin::Bidi>(bidiFlags), typeface, - text, count, // text buffer - 0, count, // draw range - 0, count, // context range - nullptr); minikin::MinikinRect rect; - layout.getBounds(&rect); + minikin::Bidi bidiFlags = static_cast<minikin::Bidi>(bidiFlagsInt); + MinikinUtils::getBounds(&paint, bidiFlags, typeface, text, count, &rect); r.fLeft = rect.mLeft; r.fTop = rect.mTop; r.fRight = rect.mRight; diff --git a/libs/hwui/jni/fonts/Font.cpp b/libs/hwui/jni/fonts/Font.cpp index 6bc318db1049..4aee6b94a2be 100644 --- a/libs/hwui/jni/fonts/Font.cpp +++ b/libs/hwui/jni/fonts/Font.cpp @@ -221,12 +221,33 @@ static jlong Font_getAxisInfo(CRITICAL_JNI_PARAMS_COMMA jlong fontHandle, jint i return (static_cast<uint64_t>(var.axisTag) << 32) | static_cast<uint64_t>(floatBinary); } +// FastNative +static jstring Font_getFontPath(JNIEnv* env, jobject, jlong fontHandle) { + const minikin::Font* font = reinterpret_cast<minikin::Font*>(fontHandle); + MinikinFontSkia* minikinSkia = static_cast<MinikinFontSkia*>(font->typeface().get()); + const std::string& filePath = minikinSkia->getFilePath(); + if (filePath.empty()) { + return nullptr; + } + return env->NewStringUTF(filePath.c_str()); +} + // Critical Native static jlong Font_getNativeFontPtr(CRITICAL_JNI_PARAMS_COMMA jlong fontHandle) { FontWrapper* font = reinterpret_cast<FontWrapper*>(fontHandle); return reinterpret_cast<jlong>(font->font.get()); } +// Critical Native +static jboolean Font_isSameBufferAddress(CRITICAL_JNI_PARAMS_COMMA jlong lFontHandle, + jlong rFontHandle) { + FontWrapper* lFont = reinterpret_cast<FontWrapper*>(lFontHandle); + FontWrapper* rFont = reinterpret_cast<FontWrapper*>(rFontHandle); + const void* lBufferPtr = lFont->font->typeface()->GetFontData(); + const void* rBufferPtr = rFont->font->typeface()->GetFontData(); + return lBufferPtr == rBufferPtr; +} + /////////////////////////////////////////////////////////////////////////////// struct FontBufferWrapper { @@ -274,7 +295,9 @@ static const JNINativeMethod gFontMethods[] = { { "nGetFontMetrics", "(JJLandroid/graphics/Paint$FontMetrics;)F", (void*) Font_getFontMetrics }, { "nGetFontInfo", "(J)J", (void*) Font_getFontInfo }, { "nGetAxisInfo", "(JI)J", (void*) Font_getAxisInfo }, + { "nGetFontPath", "(J)Ljava/lang/String;", (void*) Font_getFontPath }, { "nGetNativeFontPtr", "(J)J", (void*) Font_getNativeFontPtr }, + { "nIsSameBufferAddress", "(JJ)Z", (void*) Font_isSameBufferAddress }, }; static const JNINativeMethod gFontBufferHelperMethods[] = { diff --git a/libs/hwui/jni/text/TextShaper.cpp b/libs/hwui/jni/text/TextShaper.cpp index 9d9e91f19851..9785aa537f65 100644 --- a/libs/hwui/jni/text/TextShaper.cpp +++ b/libs/hwui/jni/text/TextShaper.cpp @@ -196,7 +196,7 @@ static const JNINativeMethod gResultMethods[] = { }; int register_android_graphics_text_TextShaper(JNIEnv* env) { - return RegisterMethodsOrDie(env, "android/graphics/text/TextShaper", gMethods, + return RegisterMethodsOrDie(env, "android/graphics/text/TextRunShaper", gMethods, NELEM(gMethods)) + RegisterMethodsOrDie(env, "android/graphics/text/PositionedGlyphs", gResultMethods, NELEM(gResultMethods)); diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp index 212a4284a824..ad6363b4452d 100644 --- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp +++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp @@ -29,7 +29,7 @@ #include <SkSurface.h> #include <SkTypes.h> -#include <GrContext.h> +#include <GrDirectContext.h> #include <GrTypes.h> #include <vk/GrVkTypes.h> diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp index 1ac99228c52c..eacabfd1dbf9 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -465,6 +465,7 @@ void CanvasContext::draw() { mCurrentFrameInfo->addFlag(FrameInfoFlags::SkippedFrame); // Notify the callbacks, even if there's nothing to draw so they aren't waiting // indefinitely + waitOnFences(); for (auto& func : mFrameCompleteCallbacks) { std::invoke(func, mFrameNumber); } diff --git a/location/java/android/location/Location.java b/location/java/android/location/Location.java index 46bd22148fb2..62b4bc173bb9 100644 --- a/location/java/android/location/Location.java +++ b/location/java/android/location/Location.java @@ -19,7 +19,6 @@ package android.location; import static java.util.concurrent.TimeUnit.NANOSECONDS; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.compat.annotation.UnsupportedAppUsage; import android.os.Build; import android.os.Bundle; @@ -75,7 +74,6 @@ public class Location implements Parcelable { * gps locations separate from other locations for coarsening. Providers that do not need to * support platforms below Android R should not use this constant. */ - @TestApi @SystemApi @Deprecated public static final String EXTRA_NO_GPS_LOCATION = "noGPSLocation"; @@ -1074,7 +1072,6 @@ public class Location implements Parcelable { * @see #isComplete * @hide */ - @TestApi @SystemApi public void makeComplete() { if (mProvider == null) mProvider = "?"; diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java index 30a4ada1b388..ac775ca05cab 100644 --- a/location/java/android/location/LocationManager.java +++ b/location/java/android/location/LocationManager.java @@ -575,7 +575,6 @@ public class LocationManager { * @hide */ @SystemApi - @TestApi @RequiresPermission(WRITE_SECURE_SETTINGS) public void setLocationEnabledForUser(boolean enabled, @NonNull UserHandle userHandle) { try { @@ -751,7 +750,6 @@ public class LocationManager { */ @Deprecated @SystemApi - @TestApi @RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION}) public void getCurrentLocation(@NonNull LocationRequest locationRequest, @Nullable CancellationSignal cancellationSignal, @@ -1192,7 +1190,6 @@ public class LocationManager { */ @Deprecated @SystemApi - @TestApi @RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION}) public void requestLocationUpdates( @Nullable LocationRequest locationRequest, @@ -1223,7 +1220,6 @@ public class LocationManager { */ @Deprecated @SystemApi - @TestApi @RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION}) public void requestLocationUpdates( @Nullable LocationRequest locationRequest, @@ -1255,7 +1251,6 @@ public class LocationManager { */ @Deprecated @SystemApi - @TestApi @RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION}) public void requestLocationUpdates( @Nullable LocationRequest locationRequest, diff --git a/location/java/android/location/LocationRequest.java b/location/java/android/location/LocationRequest.java index e03643c4c632..d0706c6b84cb 100644 --- a/location/java/android/location/LocationRequest.java +++ b/location/java/android/location/LocationRequest.java @@ -21,12 +21,12 @@ import static java.lang.Math.min; import android.Manifest; import android.annotation.FloatRange; +import android.annotation.IntDef; import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.compat.annotation.ChangeId; import android.compat.annotation.EnabledAfter; import android.compat.annotation.UnsupportedAppUsage; @@ -38,6 +38,8 @@ import android.util.TimeUtils; import com.android.internal.util.Preconditions; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.Objects; @@ -65,16 +67,48 @@ public final class LocationRequest implements Parcelable { */ public static final long PASSIVE_INTERVAL = Long.MAX_VALUE; + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef({QUALITY_LOW_POWER, QUALITY_BALANCED_POWER_ACCURACY, QUALITY_HIGH_ACCURACY}) + public @interface Quality {} + + /** + * A quality constant indicating a location provider may choose to satisfy this request by + * providing very accurate locations at the expense of potentially increased power usage. Each + * location provider may interpret this field differently, but as an example, the network + * provider may choose to return only wifi based locations rather than cell based locations in + * order to have greater accuracy when this flag is present. + */ + public static final int QUALITY_HIGH_ACCURACY = 100; + + /** + * A quality constant indicating a location provider may choose to satisfy this request by + * equally balancing power and accuracy constraints. Each location provider may interpret this + * field differently, but location providers will generally use their default behavior when this + * flag is present. + */ + public static final int QUALITY_BALANCED_POWER_ACCURACY = 102; + + /** + * A quality constant indicating a location provider may choose to satisfy this request by + * providing less accurate locations in order to save power. Each location provider may + * interpret this field differently, but as an example, the network provider may choose to + * return cell based locations rather than wifi based locations in order to save power when this + * flag is present. + */ + public static final int QUALITY_LOW_POWER = 104; + /** * Used with {@link #setQuality} to request the most accurate locations available. * * <p>This may be up to 1 meter accuracy, although this is implementation dependent. * * @hide + * @deprecated Use {@link #QUALITY_HIGH_ACCURACY} instead. */ + @Deprecated @SystemApi - @TestApi - public static final int ACCURACY_FINE = 100; + public static final int ACCURACY_FINE = QUALITY_HIGH_ACCURACY; /** * Used with {@link #setQuality} to request "block" level accuracy. @@ -84,10 +118,11 @@ public final class LocationRequest implements Parcelable { * such as this often consumes less power. * * @hide + * @deprecated Use {@link #QUALITY_BALANCED_POWER_ACCURACY} instead. */ + @Deprecated @SystemApi - @TestApi - public static final int ACCURACY_BLOCK = 102; + public static final int ACCURACY_BLOCK = QUALITY_BALANCED_POWER_ACCURACY; /** * Used with {@link #setQuality} to request "city" level accuracy. @@ -97,10 +132,11 @@ public final class LocationRequest implements Parcelable { * such as this often consumes less power. * * @hide + * @deprecated Use {@link #QUALITY_LOW_POWER} instead. */ + @Deprecated @SystemApi - @TestApi - public static final int ACCURACY_CITY = 104; + public static final int ACCURACY_CITY = QUALITY_LOW_POWER; /** * Used with {@link #setQuality} to require no direct power impact (passive locations). @@ -123,9 +159,10 @@ public final class LocationRequest implements Parcelable { * possible. * * @hide + * @deprecated Use {@link #QUALITY_LOW_POWER} instead. */ + @Deprecated @SystemApi - @TestApi public static final int POWER_LOW = 201; /** @@ -134,9 +171,10 @@ public final class LocationRequest implements Parcelable { * <p>This location request will allow high power location work. * * @hide + * @deprecated Use {@link #QUALITY_HIGH_ACCURACY} instead. */ + @Deprecated @SystemApi - @TestApi public static final int POWER_HIGH = 203; private static final long IMPLICIT_MIN_UPDATE_INTERVAL = -1; @@ -144,7 +182,7 @@ public final class LocationRequest implements Parcelable { @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, publicAlternatives = "Use {@link " + "LocationManager} methods to provide the provider explicitly.") @Nullable private String mProvider; - private int mQuality; + private @Quality int mQuality; @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, publicAlternatives = "Use {@link " + "LocationRequest} instead.") private long mInterval; @@ -168,7 +206,7 @@ public final class LocationRequest implements Parcelable { public static LocationRequest create() { // 60 minutes is the default legacy interval return new LocationRequest.Builder(60 * 60 * 1000) - .setQuality(POWER_LOW) + .setQuality(QUALITY_LOW_POWER) .build(); } @@ -241,7 +279,7 @@ public final class LocationRequest implements Parcelable { private LocationRequest( @Nullable String provider, long intervalMillis, - int quality, + @Quality int quality, long expireAtRealtimeMillis, long durationMillis, int maxUpdates, @@ -251,9 +289,6 @@ public final class LocationRequest implements Parcelable { boolean locationSettingsIgnored, boolean lowPower, WorkSource workSource) { - Preconditions.checkArgument(intervalMillis != PASSIVE_INTERVAL || quality == POWER_NONE); - Preconditions.checkArgument(minUpdateIntervalMillis <= intervalMillis); - mProvider = provider; mInterval = intervalMillis; mQuality = quality; @@ -297,24 +332,39 @@ public final class LocationRequest implements Parcelable { @SystemApi @Deprecated public @NonNull LocationRequest setQuality(int quality) { - mQuality = Builder.checkQuality(quality, true); + switch (quality) { + case POWER_HIGH: + // fall through + case ACCURACY_FINE: + mQuality = QUALITY_HIGH_ACCURACY; + break; + case ACCURACY_BLOCK: + mQuality = QUALITY_BALANCED_POWER_ACCURACY; + break; + case POWER_LOW: + // fall through + case ACCURACY_CITY: + mQuality = QUALITY_LOW_POWER; + break; + case POWER_NONE: + mInterval = PASSIVE_INTERVAL; + break; + default: + throw new IllegalArgumentException("invalid quality: " + quality); + } + return this; } /** - * Returns the quality of the location request. - * - * @return the quality of the location request + * Returns the quality hint for this location request. The quality hint informs the provider how + * it should attempt to manage any accuracy vs power tradeoffs while attempting to satisfy this + * location request. * - * @hide + * @return the desired quality tradeoffs between accuracy and power */ - @SystemApi - public int getQuality() { - if (mInterval == PASSIVE_INTERVAL) { - return POWER_NONE; - } else { - return mQuality; - } + public @Quality int getQuality() { + return mQuality; } /** @@ -570,7 +620,6 @@ public final class LocationRequest implements Parcelable { * * @hide */ - @TestApi @SystemApi public boolean isHiddenFromAppOps() { return mHideFromAppOps; @@ -596,7 +645,6 @@ public final class LocationRequest implements Parcelable { * * @hide */ - @TestApi @SystemApi public boolean isLocationSettingsIgnored() { return mLocationSettingsIgnored; @@ -632,7 +680,6 @@ public final class LocationRequest implements Parcelable { * * @hide */ - @TestApi @SystemApi public boolean isLowPower() { return mLowPower; @@ -659,7 +706,6 @@ public final class LocationRequest implements Parcelable { * * @hide */ - @TestApi @SystemApi public @NonNull WorkSource getWorkSource() { return mWorkSource; @@ -749,97 +795,65 @@ public final class LocationRequest implements Parcelable { if (mProvider != null) { s.append(mProvider).append(" "); } - if (mQuality != POWER_NONE && mQuality != ACCURACY_BLOCK) { - s.append(qualityToString(mQuality)).append(" "); - } if (mInterval != PASSIVE_INTERVAL) { s.append("@"); TimeUtils.formatDuration(mInterval, s); + + switch (mQuality) { + case QUALITY_HIGH_ACCURACY: + s.append(" HIGH_ACCURACY"); + break; + case QUALITY_BALANCED_POWER_ACCURACY: + s.append(" BALANCED"); + break; + case QUALITY_LOW_POWER: + s.append(" LOW_POWER"); + break; + } } else { s.append("PASSIVE"); } if (mExpireAtRealtimeMillis != Long.MAX_VALUE) { - s.append(" expireAt=").append(TimeUtils.formatRealtime(mExpireAtRealtimeMillis)); + s.append(", expireAt=").append(TimeUtils.formatRealtime(mExpireAtRealtimeMillis)); } if (mDurationMillis != Long.MAX_VALUE) { - s.append(" duration="); + s.append(", duration="); TimeUtils.formatDuration(mDurationMillis, s); } if (mMaxUpdates != Integer.MAX_VALUE) { - s.append(" maxUpdates=").append(mMaxUpdates); + s.append(", maxUpdates=").append(mMaxUpdates); } if (mMinUpdateIntervalMillis != IMPLICIT_MIN_UPDATE_INTERVAL && mMinUpdateIntervalMillis < mInterval) { - s.append(" minUpdateInterval="); + s.append(", minUpdateInterval="); TimeUtils.formatDuration(mMinUpdateIntervalMillis, s); } if (mMinUpdateDistanceMeters > 0.0) { - s.append(" minUpdateDistance=").append(mMinUpdateDistanceMeters); + s.append(", minUpdateDistance=").append(mMinUpdateDistanceMeters); } if (mLowPower) { - s.append(" lowPower"); + s.append(", lowPower"); } if (mHideFromAppOps) { - s.append(" hiddenFromAppOps"); + s.append(", hiddenFromAppOps"); } if (mLocationSettingsIgnored) { - s.append(" locationSettingsIgnored"); + s.append(", locationSettingsIgnored"); } - if (mWorkSource != null) { - s.append(" ").append(mWorkSource); + if (mWorkSource != null && !mWorkSource.isEmpty()) { + s.append(", ").append(mWorkSource); } s.append(']'); return s.toString(); } - private static String qualityToString(int quality) { - switch (quality) { - case ACCURACY_FINE: - return "ACCURACY_FINE"; - case ACCURACY_BLOCK: - return "ACCURACY_BLOCK"; - case ACCURACY_CITY: - return "ACCURACY_CITY"; - case POWER_NONE: - return "POWER_NONE"; - case POWER_LOW: - return "POWER_LOW"; - case POWER_HIGH: - return "POWER_HIGH"; - default: - return "???"; - } - } - /** * A builder class for {@link LocationRequest}. */ public static final class Builder { - private static int checkQuality(int quality, boolean allowDeprecated) { - switch (quality) { - case ACCURACY_FINE: - // fall through - case ACCURACY_BLOCK: - // fall through - case ACCURACY_CITY: - // fall through - case POWER_LOW: - // fall through - case POWER_HIGH: - return quality; - case POWER_NONE: - if (allowDeprecated) { - return quality; - } - // fall through - default: - throw new IllegalArgumentException("invalid quality: " + quality); - } - } - private long mIntervalMillis; - private int mQuality; + private @Quality int mQuality; private long mDurationMillis; private int mMaxUpdates; private long mMinUpdateIntervalMillis; @@ -857,7 +871,7 @@ public final class LocationRequest implements Parcelable { // gives us a range check setIntervalMillis(intervalMillis); - mQuality = ACCURACY_BLOCK; + mQuality = QUALITY_BALANCED_POWER_ACCURACY; mDurationMillis = Long.MAX_VALUE; mMaxUpdates = Integer.MAX_VALUE; mMinUpdateIntervalMillis = IMPLICIT_MIN_UPDATE_INTERVAL; @@ -885,9 +899,6 @@ public final class LocationRequest implements Parcelable { // handle edge cases that can only happen with location request that has been modified // by deprecated SystemApi methods - if (mQuality == POWER_NONE) { - mIntervalMillis = PASSIVE_INTERVAL; - } if (mIntervalMillis == PASSIVE_INTERVAL && mMinUpdateIntervalMillis == IMPLICIT_MIN_UPDATE_INTERVAL) { // this is the legacy default minimum update interval, so if we're forced to @@ -914,11 +925,17 @@ public final class LocationRequest implements Parcelable { } /** - * @hide + * Sets the request quality. The quality is a hint to providers on how they should weigh + * power vs accuracy tradeoffs. High accuracy locations may cost more power to produce, and + * lower accuracy locations may cost less power to produce. Defaults to + * {@link #QUALITY_BALANCED_POWER_ACCURACY}. */ - @SystemApi - public @NonNull Builder setQuality(int quality) { - mQuality = checkQuality(quality, false); + public @NonNull Builder setQuality(@Quality int quality) { + Preconditions.checkArgument( + quality == QUALITY_LOW_POWER || quality == QUALITY_BALANCED_POWER_ACCURACY + || quality == QUALITY_HIGH_ACCURACY, + "quality must be a defined QUALITY constant, not " + quality); + mQuality = quality; return this; } @@ -1019,7 +1036,6 @@ public final class LocationRequest implements Parcelable { * * @hide */ - @TestApi @SystemApi @RequiresPermission(Manifest.permission.UPDATE_APP_OPS_STATS) public @NonNull Builder setHiddenFromAppOps(boolean hiddenFromAppOps) { @@ -1038,7 +1054,6 @@ public final class LocationRequest implements Parcelable { * * @hide */ - @TestApi @SystemApi @RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS) public @NonNull Builder setLocationSettingsIgnored(boolean locationSettingsIgnored) { @@ -1056,7 +1071,6 @@ public final class LocationRequest implements Parcelable { * * @hide */ - @TestApi @SystemApi @RequiresPermission(Manifest.permission.LOCATION_HARDWARE) public @NonNull Builder setLowPower(boolean lowPower) { @@ -1074,7 +1088,6 @@ public final class LocationRequest implements Parcelable { * * @hide */ - @TestApi @SystemApi @RequiresPermission(Manifest.permission.UPDATE_DEVICE_STATS) public @NonNull Builder setWorkSource(@Nullable WorkSource workSource) { @@ -1102,7 +1115,7 @@ public final class LocationRequest implements Parcelable { return new LocationRequest( null, mIntervalMillis, - mIntervalMillis != PASSIVE_INTERVAL ? mQuality : POWER_NONE, + mQuality, Long.MAX_VALUE, mDurationMillis, mMaxUpdates, diff --git a/location/java/com/android/internal/location/GpsNetInitiatedHandler.java b/location/java/com/android/internal/location/GpsNetInitiatedHandler.java index eb2e23e1b5e1..4a095c9be053 100644 --- a/location/java/com/android/internal/location/GpsNetInitiatedHandler.java +++ b/location/java/com/android/internal/location/GpsNetInitiatedHandler.java @@ -28,7 +28,6 @@ import android.location.LocationManager; import android.os.RemoteException; import android.os.SystemClock; import android.os.UserHandle; -import android.telephony.PhoneNumberUtils; import android.telephony.PhoneStateListener; import android.telephony.TelephonyManager; import android.util.Log; @@ -161,7 +160,7 @@ public class GpsNetInitiatedHandler { be set to true when the phone is having emergency call, and then will be set to false by mPhoneStateListener when the emergency call ends. */ - mIsInEmergencyCall = PhoneNumberUtils.isEmergencyNumber(phoneNumber); + mIsInEmergencyCall = mTelephonyManager.isEmergencyNumber(phoneNumber); if (DEBUG) Log.v(TAG, "ACTION_NEW_OUTGOING_CALL - " + getInEmergency()); } else if (action.equals(LocationManager.MODE_CHANGED_ACTION)) { updateLocationMode(); diff --git a/location/java/com/android/internal/location/ProviderRequest.java b/location/java/com/android/internal/location/ProviderRequest.java index fee86ceb453a..00ba5523b8d4 100644 --- a/location/java/com/android/internal/location/ProviderRequest.java +++ b/location/java/com/android/internal/location/ProviderRequest.java @@ -16,10 +16,15 @@ package com.android.internal.location; +import static android.location.LocationRequest.QUALITY_BALANCED_POWER_ACCURACY; +import static android.location.LocationRequest.QUALITY_HIGH_ACCURACY; +import static android.location.LocationRequest.QUALITY_LOW_POWER; + import android.annotation.IntRange; import android.annotation.NonNull; import android.compat.annotation.UnsupportedAppUsage; import android.location.LocationRequest; +import android.location.LocationRequest.Quality; import android.os.Build; import android.os.Parcel; import android.os.Parcelable; @@ -41,7 +46,7 @@ public final class ProviderRequest implements Parcelable { public static final long INTERVAL_DISABLED = Long.MAX_VALUE; public static final ProviderRequest EMPTY_REQUEST = new ProviderRequest( - INTERVAL_DISABLED, false, false, Collections.emptyList(), new WorkSource()); + INTERVAL_DISABLED, QUALITY_BALANCED_POWER_ACCURACY, false, false, new WorkSource()); @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, publicAlternatives = "{@link " + "ProviderRequest}") @@ -49,6 +54,7 @@ public final class ProviderRequest implements Parcelable { @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, publicAlternatives = "{@link " + "ProviderRequest}") public final long interval; + private final @Quality int mQuality; private final boolean mLowPower; private final boolean mLocationSettingsIgnored; @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, publicAlternatives = "{@link " @@ -56,15 +62,24 @@ public final class ProviderRequest implements Parcelable { public final List<LocationRequest> locationRequests; private final WorkSource mWorkSource; - private ProviderRequest(long intervalMillis, boolean lowPower, - boolean locationSettingsIgnored, @NonNull List<LocationRequest> locationRequests, - @NonNull WorkSource workSource) { + private ProviderRequest(long intervalMillis, @Quality int quality, boolean lowPower, + boolean locationSettingsIgnored, @NonNull WorkSource workSource) { reportLocation = intervalMillis != INTERVAL_DISABLED; interval = intervalMillis; + mQuality = quality; mLowPower = lowPower; mLocationSettingsIgnored = locationSettingsIgnored; - this.locationRequests = locationRequests; - mWorkSource = workSource; + if (intervalMillis != INTERVAL_DISABLED) { + locationRequests = Collections.singletonList(new LocationRequest.Builder(intervalMillis) + .setQuality(quality) + .setLowPower(lowPower) + .setLocationSettingsIgnored(locationSettingsIgnored) + .setWorkSource(workSource) + .build()); + } else { + locationRequests = Collections.emptyList(); + } + mWorkSource = Objects.requireNonNull(workSource); } /** @@ -84,6 +99,15 @@ public final class ProviderRequest implements Parcelable { } /** + * The quality hint for this location request. The quality hint informs the provider how it + * should attempt to manage any accuracy vs power tradeoffs while attempting to satisfy this + * provider request. + */ + public @Quality int getQuality() { + return mQuality; + } + + /** * Whether any applicable hardware low power modes should be used to satisfy this request. */ public boolean isLowPower() { @@ -100,13 +124,6 @@ public final class ProviderRequest implements Parcelable { } /** - * The full list of location requests contributing to this provider request. - */ - public @NonNull List<LocationRequest> getLocationRequests() { - return locationRequests; - } - - /** * The power blame for this provider request. */ public @NonNull WorkSource getWorkSource() { @@ -117,13 +134,17 @@ public final class ProviderRequest implements Parcelable { new Parcelable.Creator<ProviderRequest>() { @Override public ProviderRequest createFromParcel(Parcel in) { - return new ProviderRequest( - /* intervalMillis= */ in.readLong(), - /* lowPower= */ in.readBoolean(), - /* locationSettingsIgnored= */ in.readBoolean(), - /* locationRequests= */ - in.createTypedArrayList(LocationRequest.CREATOR), - /* workSource= */ in.readTypedObject(WorkSource.CREATOR)); + long intervalMillis = in.readLong(); + if (intervalMillis == INTERVAL_DISABLED) { + return EMPTY_REQUEST; + } else { + return new ProviderRequest( + intervalMillis, + /* quality= */ in.readInt(), + /* lowPower= */ in.readBoolean(), + /* locationSettingsIgnored= */ in.readBoolean(), + /* workSource= */ in.readTypedObject(WorkSource.CREATOR)); + } } @Override @@ -140,10 +161,12 @@ public final class ProviderRequest implements Parcelable { @Override public void writeToParcel(Parcel parcel, int flags) { parcel.writeLong(interval); - parcel.writeBoolean(mLowPower); - parcel.writeBoolean(mLocationSettingsIgnored); - parcel.writeTypedList(locationRequests); - parcel.writeTypedObject(mWorkSource, flags); + if (interval != INTERVAL_DISABLED) { + parcel.writeInt(mQuality); + parcel.writeBoolean(mLowPower); + parcel.writeBoolean(mLocationSettingsIgnored); + parcel.writeTypedObject(mWorkSource, flags); + } } @Override @@ -160,16 +183,16 @@ public final class ProviderRequest implements Parcelable { return that.interval == INTERVAL_DISABLED; } else { return interval == that.interval + && mQuality == that.mQuality && mLowPower == that.mLowPower && mLocationSettingsIgnored == that.mLocationSettingsIgnored - && locationRequests.equals(that.locationRequests) && mWorkSource.equals(that.mWorkSource); } } @Override public int hashCode() { - return Objects.hash(interval, mWorkSource); + return Objects.hash(interval, mQuality, mWorkSource); } @Override @@ -179,6 +202,13 @@ public final class ProviderRequest implements Parcelable { if (interval != INTERVAL_DISABLED) { s.append("@"); TimeUtils.formatDuration(interval, s); + if (mQuality != QUALITY_BALANCED_POWER_ACCURACY) { + if (mQuality == QUALITY_HIGH_ACCURACY) { + s.append(", HIGH_ACCURACY"); + } else if (mQuality == QUALITY_LOW_POWER) { + s.append(", LOW_POWER"); + } + } if (mLowPower) { s.append(", lowPower"); } @@ -200,9 +230,9 @@ public final class ProviderRequest implements Parcelable { */ public static class Builder { private long mIntervalMillis = INTERVAL_DISABLED; + private int mQuality = QUALITY_BALANCED_POWER_ACCURACY; private boolean mLowPower; private boolean mLocationSettingsIgnored; - private List<LocationRequest> mLocationRequests = Collections.emptyList(); private WorkSource mWorkSource = new WorkSource(); /** @@ -216,6 +246,20 @@ public final class ProviderRequest implements Parcelable { } /** + * Sets the request quality. The quality is a hint to providers on how they should weigh + * power vs accuracy tradeoffs. High accuracy locations may cost more power to produce, and + * lower accuracy locations may cost less power to produce. Defaults to + * {@link LocationRequest#QUALITY_BALANCED_POWER_ACCURACY}. + */ + public @NonNull Builder setQuality(@Quality int quality) { + Preconditions.checkArgument( + quality == QUALITY_LOW_POWER || quality == QUALITY_BALANCED_POWER_ACCURACY + || quality == QUALITY_HIGH_ACCURACY); + mQuality = quality; + return this; + } + + /** * Sets whether hardware low power mode should be used. False by default. */ public @NonNull Builder setLowPower(boolean lowPower) { @@ -232,15 +276,6 @@ public final class ProviderRequest implements Parcelable { } /** - * Sets the {@link LocationRequest}s associated with this request. Empty by default. - */ - public @NonNull Builder setLocationRequests( - @NonNull List<LocationRequest> locationRequests) { - this.mLocationRequests = Objects.requireNonNull(locationRequests); - return this; - } - - /** * Sets the work source for power blame. Empty by default. */ public @NonNull Builder setWorkSource(@NonNull WorkSource workSource) { @@ -255,8 +290,8 @@ public final class ProviderRequest implements Parcelable { if (mIntervalMillis == INTERVAL_DISABLED) { return EMPTY_REQUEST; } else { - return new ProviderRequest(mIntervalMillis, mLowPower, mLocationSettingsIgnored, - mLocationRequests, mWorkSource); + return new ProviderRequest(mIntervalMillis, mQuality, mLowPower, + mLocationSettingsIgnored, mWorkSource); } } } diff --git a/location/lib/api/current.txt b/location/lib/api/current.txt index f43eb639ee23..80636c665a1a 100644 --- a/location/lib/api/current.txt +++ b/location/lib/api/current.txt @@ -29,18 +29,18 @@ package com.android.location.provider { field public static final String FUSED_PROVIDER = "fused"; } - public final class LocationRequestUnbundled { - method public long getFastestInterval(); - method public long getInterval(); - method public int getQuality(); - method public float getSmallestDisplacement(); - method public boolean isLocationSettingsIgnored(); - field public static final int ACCURACY_BLOCK = 102; // 0x66 - field public static final int ACCURACY_CITY = 104; // 0x68 - field public static final int ACCURACY_FINE = 100; // 0x64 - field public static final int POWER_HIGH = 203; // 0xcb - field public static final int POWER_LOW = 201; // 0xc9 - field public static final int POWER_NONE = 200; // 0xc8 + @Deprecated public final class LocationRequestUnbundled { + method @Deprecated public long getFastestInterval(); + method @Deprecated public long getInterval(); + method @Deprecated @android.location.LocationRequest.Quality public int getQuality(); + method @Deprecated public float getSmallestDisplacement(); + method @Deprecated public boolean isLocationSettingsIgnored(); + field @Deprecated public static final int ACCURACY_BLOCK = 102; // 0x66 + field @Deprecated public static final int ACCURACY_CITY = 104; // 0x68 + field @Deprecated public static final int ACCURACY_FINE = 100; // 0x64 + field @Deprecated public static final int POWER_HIGH = 203; // 0xcb + field @Deprecated public static final int POWER_LOW = 201; // 0xc9 + field @Deprecated public static final int POWER_NONE = 200; // 0xc8 } public final class ProviderPropertiesUnbundled { @@ -49,7 +49,8 @@ package com.android.location.provider { public final class ProviderRequestUnbundled { method public long getInterval(); - method @NonNull public java.util.List<com.android.location.provider.LocationRequestUnbundled> getLocationRequests(); + method @Deprecated @NonNull public java.util.List<com.android.location.provider.LocationRequestUnbundled> getLocationRequests(); + method @android.location.LocationRequest.Quality @RequiresApi(android.os.Build.VERSION_CODES.S) public int getQuality(); method public boolean getReportLocation(); method @NonNull @RequiresApi(android.os.Build.VERSION_CODES.S) public android.os.WorkSource getWorkSource(); method @RequiresApi(android.os.Build.VERSION_CODES.Q) public boolean isLocationSettingsIgnored(); diff --git a/location/lib/java/com/android/location/provider/LocationRequestUnbundled.java b/location/lib/java/com/android/location/provider/LocationRequestUnbundled.java index 92e05ef68e3b..0e7c633eb5fb 100644 --- a/location/lib/java/com/android/location/provider/LocationRequestUnbundled.java +++ b/location/lib/java/com/android/location/provider/LocationRequestUnbundled.java @@ -17,6 +17,7 @@ package com.android.location.provider; import android.location.LocationRequest; +import android.location.LocationRequest.Quality; /** * This class is an interface to LocationRequests for unbundled applications. @@ -24,55 +25,50 @@ import android.location.LocationRequest; * <p>IMPORTANT: This class is effectively a public API for unbundled * applications, and must remain API stable. See README.txt in the root * of this package for more information. + * + * @deprecated Do not use. */ +@Deprecated public final class LocationRequestUnbundled { + /** - * Returned by {@link #getQuality} when requesting the most accurate locations available. - * - * <p>This may be up to 1 meter accuracy, although this is implementation dependent. + * @deprecated Use {@link LocationRequest#QUALITY_HIGH_ACCURACY} instead. */ + @Deprecated public static final int ACCURACY_FINE = LocationRequest.ACCURACY_FINE; /** - * Returned by {@link #getQuality} when requesting "block" level accuracy. - * - * <p>Block level accuracy is considered to be about 100 meter accuracy, - * although this is implementation dependent. Using a coarse accuracy - * such as this often consumes less power. + * @deprecated Use {@link LocationRequest#QUALITY_BALANCED_POWER_ACCURACY} instead. */ + @Deprecated public static final int ACCURACY_BLOCK = LocationRequest.ACCURACY_BLOCK; + /** - * Returned by {@link #getQuality} when requesting "city" level accuracy. - * - * <p>City level accuracy is considered to be about 10km accuracy, - * although this is implementation dependent. Using a coarse accuracy - * such as this often consumes less power. + * @deprecated Use {@link LocationRequest#QUALITY_LOW_POWER} instead. */ + @Deprecated public static final int ACCURACY_CITY = LocationRequest.ACCURACY_CITY; + /** - * Returned by {@link #getQuality} when requiring no direct power impact (passive locations). - * - * <p>This location request will not trigger any active location requests, - * but will receive locations triggered by other applications. Your application - * will not receive any direct power blame for location work. + * @deprecated Do not use. */ + @Deprecated public static final int POWER_NONE = LocationRequest.POWER_NONE; + /** - * Returned by {@link #getQuality} when requesting low power impact. - * - * <p>This location request will avoid high power location work where - * possible. + * @deprecated Use {@link LocationRequest#QUALITY_LOW_POWER} instead. */ + @Deprecated public static final int POWER_LOW = LocationRequest.POWER_LOW; + /** - * Returned by {@link #getQuality} when allowing high power consumption for location. - * - * <p>This location request will allow high power location work. + * @deprecated Use {@link LocationRequest#QUALITY_BALANCED_POWER_ACCURACY} instead. */ + @Deprecated public static final int POWER_HIGH = LocationRequest.POWER_HIGH; private final LocationRequest delegate; @@ -102,9 +98,9 @@ public final class LocationRequestUnbundled { /** * Get the quality of the request. * - * @return an accuracy or power constant + * @return a {@link LocationRequest} QUALITY_* constant */ - public int getQuality() { + public @Quality int getQuality() { return delegate.getQuality(); } diff --git a/location/lib/java/com/android/location/provider/ProviderRequestUnbundled.java b/location/lib/java/com/android/location/provider/ProviderRequestUnbundled.java index 6f5fcc7bfc8e..f7bac74bf92d 100644 --- a/location/lib/java/com/android/location/provider/ProviderRequestUnbundled.java +++ b/location/lib/java/com/android/location/provider/ProviderRequestUnbundled.java @@ -25,7 +25,7 @@ import androidx.annotation.RequiresApi; import com.android.internal.location.ProviderRequest; -import java.util.ArrayList; +import java.util.Collections; import java.util.List; /** @@ -57,6 +57,16 @@ public final class ProviderRequestUnbundled { } /** + * The quality hint for this location request. The quality hint informs the provider how it + * should attempt to manage any accuracy vs power tradeoffs while attempting to satisfy this + * provider request. + */ + @RequiresApi(Build.VERSION_CODES.S) + public @LocationRequest.Quality int getQuality() { + return mRequest.getQuality(); + } + + /** * The interval at which a provider should report location. Will return * {@link #INTERVAL_DISABLED} for an inactive request. */ @@ -84,14 +94,22 @@ public final class ProviderRequestUnbundled { /** * The full list of location requests contributing to this provider request. + * + * @deprecated Do not use. */ + @Deprecated public @NonNull List<LocationRequestUnbundled> getLocationRequests() { - List<LocationRequestUnbundled> result = new ArrayList<>( - mRequest.getLocationRequests().size()); - for (LocationRequest r : mRequest.getLocationRequests()) { - result.add(new LocationRequestUnbundled(r)); + if (!mRequest.isActive()) { + return Collections.emptyList(); } - return result; + + return Collections.singletonList(new LocationRequestUnbundled( + new LocationRequest.Builder(mRequest.getIntervalMillis()) + .setQuality(mRequest.getQuality()) + .setLowPower(mRequest.isLowPower()) + .setLocationSettingsIgnored(mRequest.isLocationSettingsIgnored()) + .setWorkSource(mRequest.getWorkSource()) + .build())); } /** diff --git a/media/OWNERS b/media/OWNERS index 0fc781c848f7..e74149019b11 100644 --- a/media/OWNERS +++ b/media/OWNERS @@ -1,4 +1,3 @@ -andrewlewis@google.com chz@google.com elaurent@google.com essick@google.com @@ -18,5 +17,12 @@ marcone@google.com nchalko@google.com philburk@google.com quxiangfang@google.com -sungsoo@google.com wonsik@google.com + +# LON +andrewlewis@google.com +aquilescanta@google.com +olly@google.com + +# SEO +sungsoo@google.com diff --git a/media/java/android/media/AudioFocusInfo.java b/media/java/android/media/AudioFocusInfo.java index 675cf7360b82..3647b6ebcfbc 100644 --- a/media/java/android/media/AudioFocusInfo.java +++ b/media/java/android/media/AudioFocusInfo.java @@ -19,7 +19,6 @@ package android.media; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.os.Parcel; import android.os.Parcelable; @@ -29,7 +28,6 @@ import java.util.Objects; * @hide * A class to encapsulate information about an audio focus owner or request. */ -@TestApi @SystemApi public final class AudioFocusInfo implements Parcelable { diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java index 22b5ca53e7e9..195122db146a 100644 --- a/media/java/android/media/AudioManager.java +++ b/media/java/android/media/AudioManager.java @@ -3916,7 +3916,6 @@ public class AudioManager { * @param requestResult the result to the focus request to be passed to the requester * @param ap a valid registered {@link AudioPolicy} configured as a focus policy. */ - @TestApi @SystemApi @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void setFocusRequestResult(@NonNull AudioFocusInfo afi, @@ -3956,7 +3955,6 @@ public class AudioManager { * if there was an error sending the request. * @throws NullPointerException if the {@link AudioFocusInfo} or {@link AudioPolicy} are null. */ - @TestApi @SystemApi @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int dispatchAudioFocusChange(@NonNull AudioFocusInfo afi, int focusChange, @@ -4219,7 +4217,6 @@ public class AudioManager { * {@link android.Manifest.permission#MODIFY_AUDIO_ROUTING} permission, * {@link #SUCCESS} otherwise. */ - @TestApi @SystemApi @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int registerAudioPolicy(@NonNull AudioPolicy policy) { @@ -4254,7 +4251,6 @@ public class AudioManager { * Unregisters an {@link AudioPolicy} asynchronously. * @param policy the non-null {@link AudioPolicy} to unregister. */ - @TestApi @SystemApi @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void unregisterAudioPolicyAsync(@NonNull AudioPolicy policy) { @@ -4281,7 +4277,6 @@ public class AudioManager { * associated with mixes of this policy. * @param policy the non-null {@link AudioPolicy} to unregister. */ - @TestApi @SystemApi @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void unregisterAudioPolicy(@NonNull AudioPolicy policy) { @@ -5619,7 +5614,6 @@ public class AudioManager { */ /** @hide */ - @TestApi @SystemApi public static final int SUCCESS = AudioSystem.SUCCESS; /** diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java index de2a7b2c15e3..fa6a4ff5af67 100644 --- a/media/java/android/media/AudioTrack.java +++ b/media/java/android/media/AudioTrack.java @@ -1263,14 +1263,20 @@ public class AudioTrack extends PlayerBase // TODO: Check mEncapsulationMode compatibility with MODE_STATIC, etc? - try { - // If the buffer size is not specified in streaming mode, - // use a single frame for the buffer size and let the - // native code figure out the minimum buffer size. - if (mMode == MODE_STREAM && mBufferSizeInBytes == 0) { - mBufferSizeInBytes = mFormat.getChannelCount() - * mFormat.getBytesPerSample(mFormat.getEncoding()); + // If the buffer size is not specified in streaming mode, + // use a single frame for the buffer size and let the + // native code figure out the minimum buffer size. + if (mMode == MODE_STREAM && mBufferSizeInBytes == 0) { + int bytesPerSample = 1; + try { + bytesPerSample = mFormat.getBytesPerSample(mFormat.getEncoding()); + } catch (IllegalArgumentException e) { + // do nothing } + mBufferSizeInBytes = mFormat.getChannelCount() * bytesPerSample; + } + + try { final AudioTrack track = new AudioTrack( mAttributes, mFormat, mBufferSizeInBytes, mMode, mSessionId, mOffload, mEncapsulationMode, mTunerConfiguration); diff --git a/media/java/android/media/ExifInterface.java b/media/java/android/media/ExifInterface.java index 8845d6954db2..44890bee2291 100644 --- a/media/java/android/media/ExifInterface.java +++ b/media/java/android/media/ExifInterface.java @@ -31,6 +31,8 @@ import android.compat.annotation.UnsupportedAppUsage; import android.content.res.AssetManager; import android.graphics.Bitmap; import android.graphics.BitmapFactory; +import android.os.FileUtils; +import android.os.SystemProperties; import android.system.ErrnoException; import android.system.Os; import android.system.OsConstants; @@ -70,7 +72,6 @@ import java.util.Locale; import java.util.Map; import java.util.Set; import java.util.TimeZone; -import java.util.UUID; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.zip.CRC32; @@ -1524,6 +1525,11 @@ public class ExifInterface { if (fileDescriptor == null) { throw new NullPointerException("fileDescriptor cannot be null"); } + boolean optimize = SystemProperties.getBoolean("fuse.sys.transcode_exif_optimize", false); + FileDescriptor modernFd = optimize ? FileUtils.convertToModernFd(fileDescriptor) : null; + if (modernFd != null) { + fileDescriptor = modernFd; + } mAssetInputStream = null; mFilename = null; @@ -2088,28 +2094,18 @@ public class ExifInterface { FileInputStream in = null; FileOutputStream out = null; - File originalFile = null; - if (mFilename != null) { - originalFile = new File(mFilename); - } File tempFile = null; try { - // Move the original file to temporary file. + // Copy the original file to temporary file. + tempFile = File.createTempFile("temp", "tmp"); if (mFilename != null) { - String parent = originalFile.getParent(); - String name = originalFile.getName(); - String tempPrefix = UUID.randomUUID().toString() + "_"; - tempFile = new File(parent, tempPrefix + name); - if (!originalFile.renameTo(tempFile)) { - throw new IOException("Couldn't rename to " + tempFile.getAbsolutePath()); - } + in = new FileInputStream(mFilename); } else if (mSeekableFileDescriptor != null) { - tempFile = File.createTempFile("temp", "tmp"); Os.lseek(mSeekableFileDescriptor, 0, OsConstants.SEEK_SET); in = new FileInputStream(mSeekableFileDescriptor); - out = new FileOutputStream(tempFile); - copy(in, out); } + out = new FileOutputStream(tempFile); + copy(in, out); } catch (Exception e) { throw new IOException("Failed to copy original file to temp file", e); } finally { @@ -2139,12 +2135,23 @@ public class ExifInterface { } } } catch (Exception e) { + // Restore original file + in = new FileInputStream(tempFile); if (mFilename != null) { - if (!tempFile.renameTo(originalFile)) { - throw new IOException("Couldn't restore original file: " - + originalFile.getAbsolutePath()); + out = new FileOutputStream(mFilename); + } else if (mSeekableFileDescriptor != null) { + try { + Os.lseek(mSeekableFileDescriptor, 0, OsConstants.SEEK_SET); + } catch (ErrnoException exception) { + throw new IOException("Failed to save new file. Original file may be " + + "corrupted since error occurred while trying to restore it.", + exception); } + out = new FileOutputStream(mSeekableFileDescriptor); } + copy(in, out); + closeQuietly(in); + closeQuietly(out); throw new IOException("Failed to save new file", e); } finally { closeQuietly(in); @@ -2533,11 +2540,20 @@ public class ExifInterface { private void initForFilename(String filename) throws IOException { FileInputStream in = null; + FileInputStream legacyInputStream = null; mAssetInputStream = null; mFilename = filename; mIsInputStream = false; try { in = new FileInputStream(filename); + boolean optimize = SystemProperties.getBoolean("fuse.sys.transcode_exif_optimize", + false); + FileDescriptor modernFd = optimize ? FileUtils.convertToModernFd(in.getFD()) : null; + if (modernFd != null) { + legacyInputStream = in; + in = new FileInputStream(modernFd); + } + if (isSeekableFD(in.getFD())) { mSeekableFileDescriptor = in.getFD(); } else { @@ -2546,6 +2562,7 @@ public class ExifInterface { loadAttributes(in); } finally { closeQuietly(in); + closeQuietly(legacyInputStream); } } diff --git a/media/java/android/media/MediaMetadataRetriever.java b/media/java/android/media/MediaMetadataRetriever.java index 835a7091bb70..ca8b9b936e99 100644 --- a/media/java/android/media/MediaMetadataRetriever.java +++ b/media/java/android/media/MediaMetadataRetriever.java @@ -30,7 +30,10 @@ import android.content.res.AssetFileDescriptor; import android.graphics.Bitmap; import android.net.Uri; import android.os.Build; +import android.os.Bundle; +import android.os.FileUtils; import android.os.IBinder; +import android.os.SystemProperties; import android.text.TextUtils; import java.io.FileDescriptor; @@ -48,7 +51,6 @@ import java.util.Map; * frame and meta data from an input media file. */ public class MediaMetadataRetriever implements AutoCloseable { - // borrowed from ExoPlayer private static final String[] STANDARD_GENRES = new String[] { // These are the official ID3v1 genres. @@ -296,7 +298,19 @@ public class MediaMetadataRetriever implements AutoCloseable { * non-negative. * @throws IllegalArgumentException if the arguments are invalid */ - public native void setDataSource(FileDescriptor fd, long offset, long length) + public void setDataSource(FileDescriptor fd, long offset, long length) + throws IllegalArgumentException { + boolean optimize = SystemProperties.getBoolean("fuse.sys.transcode_retriever_optimize", + false); + FileDescriptor modernFd = optimize ? FileUtils.convertToModernFd(fd) : null; + if (modernFd == null) { + _setDataSource(fd, offset, length); + } else { + _setDataSource(modernFd, offset, length); + } + } + + private native void _setDataSource(FileDescriptor fd, long offset, long length) throws IllegalArgumentException; /** @@ -340,7 +354,12 @@ public class MediaMetadataRetriever implements AutoCloseable { try { ContentResolver resolver = context.getContentResolver(); try { - fd = resolver.openAssetFileDescriptor(uri, "r"); + boolean optimize = + SystemProperties.getBoolean("fuse.sys.transcode_retriever_optimize", false); + Bundle opts = new Bundle(); + opts.putBoolean("android.provider.extra.ACCEPT_ORIGINAL_MEDIA_FORMAT", true); + fd = optimize ? resolver.openTypedAssetFileDescriptor(uri, "*/*", opts) + : resolver.openAssetFileDescriptor(uri, "r"); } catch(FileNotFoundException e) { throw new IllegalArgumentException("could not access " + uri); } diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java index 851c1ec8ec1f..47d276a50034 100644 --- a/media/java/android/media/MediaPlayer.java +++ b/media/java/android/media/MediaPlayer.java @@ -31,6 +31,8 @@ import android.graphics.SurfaceTexture; import android.media.SubtitleController.Anchor; import android.media.SubtitleTrack.RenderingWidget; import android.net.Uri; +import android.os.Bundle; +import android.os.FileUtils; import android.os.Handler; import android.os.HandlerThread; import android.os.IBinder; @@ -1104,7 +1106,13 @@ public class MediaPlayer extends PlayerBase } private boolean attemptDataSource(ContentResolver resolver, Uri uri) { - try (AssetFileDescriptor afd = resolver.openAssetFileDescriptor(uri, "r")) { + boolean optimize = SystemProperties.getBoolean("fuse.sys.transcode_player_optimize", + false); + Bundle opts = new Bundle(); + opts.putBoolean("android.provider.extra.ACCEPT_ORIGINAL_MEDIA_FORMAT", true); + try (AssetFileDescriptor afd = optimize + ? resolver.openTypedAssetFileDescriptor(uri, "*/*", opts) + : resolver.openAssetFileDescriptor(uri, "r")) { setDataSource(afd); return true; } catch (NullPointerException | SecurityException | IOException ex) { @@ -1245,7 +1253,13 @@ public class MediaPlayer extends PlayerBase */ public void setDataSource(FileDescriptor fd, long offset, long length) throws IOException, IllegalArgumentException, IllegalStateException { - _setDataSource(fd, offset, length); + boolean optimize = SystemProperties.getBoolean("fuse.sys.transcode_player_optimize", false); + FileDescriptor modernFd = optimize ? FileUtils.convertToModernFd(fd) : null; + if (modernFd == null) { + _setDataSource(fd, offset, length); + } else { + _setDataSource(modernFd, offset, length); + } } private native void _setDataSource(FileDescriptor fd, long offset, long length) @@ -2899,8 +2913,13 @@ public class MediaPlayer extends PlayerBase AssetFileDescriptor fd = null; try { + boolean optimize = SystemProperties.getBoolean("fuse.sys.transcode_player_optimize", + false); ContentResolver resolver = context.getContentResolver(); - fd = resolver.openAssetFileDescriptor(uri, "r"); + Bundle opts = new Bundle(); + opts.putBoolean("android.provider.extra.ACCEPT_ORIGINAL_MEDIA_FORMAT", true); + fd = optimize ? resolver.openTypedAssetFileDescriptor(uri, "*/*", opts) + : resolver.openAssetFileDescriptor(uri, "r"); if (fd == null) { return; } diff --git a/media/java/android/media/MediaTranscodeManager.java b/media/java/android/media/MediaTranscodeManager.java index 8cbe52f7c481..046403670693 100644 --- a/media/java/android/media/MediaTranscodeManager.java +++ b/media/java/android/media/MediaTranscodeManager.java @@ -22,7 +22,6 @@ import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.content.ContentResolver; import android.content.Context; import android.content.res.AssetFileDescriptor; @@ -101,7 +100,6 @@ import java.util.concurrent.Executors; TODO(hkuang): Clarify whether supports framerate conversion. @hide */ -@TestApi @SystemApi public final class MediaTranscodeManager { private static final String TAG = "MediaTranscodeManager"; @@ -443,7 +441,7 @@ public final class MediaTranscodeManager { } @Override - public void onAwaitNumberOfJobsChanged(int jobId, int oldAwaitNumber, + public void onAwaitNumberOfSessionsChanged(int jobId, int oldAwaitNumber, int newAwaitNumber) throws RemoteException { //TODO(hkuang): Implement this. } @@ -1081,7 +1079,7 @@ public final class MediaTranscodeManager { private final MediaTranscodeManager mManager; private Executor mListenerExecutor; private OnTranscodingFinishedListener mListener; - private int mJobId = -1; + private int mSessionId = -1; // Lock for internal state. private final Object mLock = new Object(); @GuardedBy("mLock") @@ -1104,7 +1102,7 @@ public final class MediaTranscodeManager { private TranscodingJob( @NonNull MediaTranscodeManager manager, @NonNull TranscodingRequest request, - @NonNull TranscodingJobParcel parcel, + @NonNull TranscodingSessionParcel parcel, @NonNull @CallbackExecutor Executor executor, @NonNull OnTranscodingFinishedListener listener) { Objects.requireNonNull(manager, "manager must not be null"); @@ -1112,7 +1110,7 @@ public final class MediaTranscodeManager { Objects.requireNonNull(executor, "listenerExecutor must not be null"); Objects.requireNonNull(listener, "listener must not be null"); mManager = manager; - mJobId = parcel.jobId; + mSessionId = parcel.sessionId; mListenerExecutor = executor; mListener = listener; mRequest = request; @@ -1196,16 +1194,16 @@ public final class MediaTranscodeManager { synchronized (mManager.mPendingTranscodingJobs) { try { // Submits the request to MediaTranscoding service. - TranscodingJobParcel jobParcel = new TranscodingJobParcel(); - if (!client.submitRequest(mRequest.writeToParcel(), jobParcel)) { + TranscodingSessionParcel sessionParcel = new TranscodingSessionParcel(); + if (!client.submitRequest(mRequest.writeToParcel(), sessionParcel)) { mHasRetried = true; throw new UnsupportedOperationException("Failed to enqueue request"); } // Replace the old job id wit the new one. - mJobId = jobParcel.jobId; + mSessionId = sessionParcel.sessionId; // Adds the new job back into pending jobs. - mManager.mPendingTranscodingJobs.put(mJobId, this); + mManager.mPendingTranscodingJobs.put(mSessionId, this); } catch (RemoteException re) { throw new MediaTranscodingException.ServiceNotAvailableException( "Failed to resubmit request to Transcoding service"); @@ -1229,7 +1227,7 @@ public final class MediaTranscodeManager { ITranscodingClient client = mManager.getTranscodingClient(); // The client may be gone. if (client != null) { - client.cancelJob(mJobId); + client.cancelSession(mSessionId); } } catch (RemoteException re) { //TODO(hkuang): Find out what to do if failing to cancel the job. @@ -1272,7 +1270,7 @@ public final class MediaTranscodeManager { * @return job id. */ public int getJobId() { - return mJobId; + return mSessionId; } /** @@ -1326,7 +1324,7 @@ public final class MediaTranscodeManager { break; } return String.format(" Job: {id: %d, status: %s, result: %s, progress: %d}", - mJobId, status, result, mProgress); + mSessionId, status, result, mProgress); } private void updateProgress(int newProgress) { @@ -1383,7 +1381,7 @@ public final class MediaTranscodeManager { // Submits the request to MediaTranscoding service. try { - TranscodingJobParcel jobParcel = new TranscodingJobParcel(); + TranscodingSessionParcel sessionParcel = new TranscodingSessionParcel(); // Synchronizes the access to mPendingTranscodingJobs to make sure the job Id is // inserted in the mPendingTranscodingJobs in the callback handler. synchronized (mPendingTranscodingJobs) { @@ -1399,15 +1397,15 @@ public final class MediaTranscodeManager { } } - if (!mTranscodingClient.submitRequest(requestParcel, jobParcel)) { + if (!mTranscodingClient.submitRequest(requestParcel, sessionParcel)) { throw new UnsupportedOperationException("Failed to enqueue request"); } } - // Wraps the TranscodingJobParcel into a TranscodingJob and returns it to client for + // Wraps the TranscodingSessionParcel into a TranscodingJob and returns it to client for // tracking. TranscodingJob job = new TranscodingJob(this, transcodingRequest, - jobParcel, + sessionParcel, listenerExecutor, listener); diff --git a/media/java/android/media/audiopolicy/AudioMix.java b/media/java/android/media/audiopolicy/AudioMix.java index 61113bc858db..4e451c6e42b1 100644 --- a/media/java/android/media/audiopolicy/AudioMix.java +++ b/media/java/android/media/audiopolicy/AudioMix.java @@ -19,7 +19,6 @@ package android.media.audiopolicy; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.compat.annotation.UnsupportedAppUsage; import android.media.AudioDeviceInfo; import android.media.AudioFormat; @@ -32,7 +31,6 @@ import java.util.Objects; /** * @hide */ -@TestApi @SystemApi public class AudioMix { diff --git a/media/java/android/media/audiopolicy/AudioMixingRule.java b/media/java/android/media/audiopolicy/AudioMixingRule.java index 68c9593d102d..f6f982a4ebe2 100644 --- a/media/java/android/media/audiopolicy/AudioMixingRule.java +++ b/media/java/android/media/audiopolicy/AudioMixingRule.java @@ -18,7 +18,6 @@ package android.media.audiopolicy; import android.annotation.NonNull; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.compat.annotation.UnsupportedAppUsage; import android.media.AudioAttributes; import android.os.Parcel; @@ -42,7 +41,6 @@ import java.util.Objects; * .build(); * </pre> */ -@TestApi @SystemApi public class AudioMixingRule { diff --git a/media/java/android/media/audiopolicy/AudioPolicy.java b/media/java/android/media/audiopolicy/AudioPolicy.java index 8a17465c53b3..3e8d76ac7551 100644 --- a/media/java/android/media/audiopolicy/AudioPolicy.java +++ b/media/java/android/media/audiopolicy/AudioPolicy.java @@ -58,7 +58,6 @@ import java.util.Objects; * @hide * AudioPolicy provides access to the management of audio routing and audio focus. */ -@TestApi @SystemApi public class AudioPolicy { @@ -418,7 +417,6 @@ public class AudioPolicy { * @param devices list of devices to which the audio stream of the application may be routed. * @return true if the change was successful, false otherwise. */ - @TestApi @SystemApi public boolean setUidDeviceAffinity(int uid, @NonNull List<AudioDeviceInfo> devices) { if (devices == null) { @@ -460,7 +458,6 @@ public class AudioPolicy { * @param uid UID of the application affected. * @return true if the change was successful, false otherwise. */ - @TestApi @SystemApi public boolean removeUidDeviceAffinity(int uid) { synchronized (mLock) { @@ -486,7 +483,6 @@ public class AudioPolicy { * {@link UserHandle#getIdentifier}. Not to be confused with application uid. * @return true if the change was successful, false otherwise. */ - @TestApi @SystemApi public boolean removeUserIdDeviceAffinity(@UserIdInt int userId) { synchronized (mLock) { @@ -519,7 +515,6 @@ public class AudioPolicy { * @param devices list of devices to which the audio stream of the application may be routed. * @return true if the change was successful, false otherwise. */ - @TestApi @SystemApi public boolean setUserIdDeviceAffinity(@UserIdInt int userId, @NonNull List<AudioDeviceInfo> devices) { diff --git a/media/java/android/media/musicrecognition/MusicRecognitionManager.java b/media/java/android/media/musicrecognition/MusicRecognitionManager.java index c7f55dfe1c76..6bbcfd3b5b66 100644 --- a/media/java/android/media/musicrecognition/MusicRecognitionManager.java +++ b/media/java/android/media/musicrecognition/MusicRecognitionManager.java @@ -25,7 +25,6 @@ import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.annotation.SystemService; -import android.annotation.TestApi; import android.content.Context; import android.media.MediaMetadata; import android.os.Bundle; @@ -41,7 +40,6 @@ import java.util.concurrent.Executor; * @hide */ @SystemApi -@TestApi @SystemService(Context.MUSIC_RECOGNITION_SERVICE) public class MusicRecognitionManager { diff --git a/media/java/android/media/musicrecognition/MusicRecognitionService.java b/media/java/android/media/musicrecognition/MusicRecognitionService.java index b75d2c4f1e50..e2071b84c00b 100644 --- a/media/java/android/media/musicrecognition/MusicRecognitionService.java +++ b/media/java/android/media/musicrecognition/MusicRecognitionService.java @@ -21,7 +21,6 @@ import static com.android.internal.util.function.pooled.PooledLambda.obtainMessa import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.app.Service; import android.content.Intent; import android.media.AudioFormat; @@ -41,7 +40,6 @@ import android.util.Log; * @hide */ @SystemApi -@TestApi public abstract class MusicRecognitionService extends Service { private static final String TAG = MusicRecognitionService.class.getSimpleName(); diff --git a/media/java/android/media/musicrecognition/RecognitionRequest.java b/media/java/android/media/musicrecognition/RecognitionRequest.java index 65b2ccd37f79..e4f484873cd4 100644 --- a/media/java/android/media/musicrecognition/RecognitionRequest.java +++ b/media/java/android/media/musicrecognition/RecognitionRequest.java @@ -23,7 +23,6 @@ import static java.util.Objects.requireNonNull; import android.annotation.NonNull; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.media.AudioAttributes; import android.media.AudioFormat; import android.media.AudioRecord; @@ -37,7 +36,6 @@ import android.os.Parcelable; * @hide */ @SystemApi -@TestApi public final class RecognitionRequest implements Parcelable { @NonNull private final AudioAttributes mAudioAttributes; @NonNull private final AudioFormat mAudioFormat; @@ -82,7 +80,6 @@ public final class RecognitionRequest implements Parcelable { * @hide */ @SystemApi - @TestApi public static final class Builder { private AudioFormat mAudioFormat = new AudioFormat.Builder() .setSampleRate(16000) diff --git a/media/java/android/media/session/MediaSession.java b/media/java/android/media/session/MediaSession.java index e17e069d61d0..f582d2addf17 100644 --- a/media/java/android/media/session/MediaSession.java +++ b/media/java/android/media/session/MediaSession.java @@ -609,25 +609,6 @@ public final class MediaSession { } /** - * Return true if this is considered an active playback state. - * - * @hide - */ - public static boolean isActiveState(int state) { - switch (state) { - case PlaybackState.STATE_FAST_FORWARDING: - case PlaybackState.STATE_REWINDING: - case PlaybackState.STATE_SKIPPING_TO_PREVIOUS: - case PlaybackState.STATE_SKIPPING_TO_NEXT: - case PlaybackState.STATE_BUFFERING: - case PlaybackState.STATE_CONNECTING: - case PlaybackState.STATE_PLAYING: - return true; - } - return false; - } - - /** * Returns whether the given bundle includes non-framework Parcelables. */ static boolean hasCustomParcelable(@Nullable Bundle bundle) { diff --git a/media/java/android/media/session/PlaybackState.java b/media/java/android/media/session/PlaybackState.java index 8dd61276f75f..b1a88ed16bcd 100644 --- a/media/java/android/media/session/PlaybackState.java +++ b/media/java/android/media/session/PlaybackState.java @@ -19,6 +19,7 @@ import android.annotation.DrawableRes; import android.annotation.IntDef; import android.annotation.LongDef; import android.annotation.Nullable; +import android.annotation.SystemApi; import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; @@ -480,6 +481,25 @@ public final class PlaybackState implements Parcelable { return mExtras; } + /** + * Returns whether this is considered as an active playback state. + * @hide + */ + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) + public boolean isActiveState() { + switch (mState) { + case PlaybackState.STATE_FAST_FORWARDING: + case PlaybackState.STATE_REWINDING: + case PlaybackState.STATE_SKIPPING_TO_PREVIOUS: + case PlaybackState.STATE_SKIPPING_TO_NEXT: + case PlaybackState.STATE_BUFFERING: + case PlaybackState.STATE_CONNECTING: + case PlaybackState.STATE_PLAYING: + return true; + } + return false; + } + public static final @android.annotation.NonNull Parcelable.Creator<PlaybackState> CREATOR = new Parcelable.Creator<PlaybackState>() { @Override diff --git a/media/java/android/media/tv/TunedInfo.java b/media/java/android/media/tv/TunedInfo.java index 6fc57841ed3b..6199c89fe4fa 100644 --- a/media/java/android/media/tv/TunedInfo.java +++ b/media/java/android/media/tv/TunedInfo.java @@ -19,6 +19,7 @@ package android.media.tv; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.SystemApi; import android.net.Uri; import android.os.Parcel; import android.os.Parcelable; @@ -35,6 +36,7 @@ import java.util.Objects; * or pass-through input. * @hide */ +@SystemApi public final class TunedInfo implements Parcelable { static final String TAG = "TunedInfo"; diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java index 73539fb8a014..e9959be249ec 100644 --- a/media/java/android/media/tv/TvInputManager.java +++ b/media/java/android/media/tv/TvInputManager.java @@ -906,8 +906,8 @@ public final class TvInputManager { * @param tunedInfos a list of {@link TunedInfo} objects of new tuned information. * @hide */ - public void onCurrentTunedInfosUpdated( - @NonNull List<TunedInfo> tunedInfos) { + @SystemApi + public void onCurrentTunedInfosUpdated(@NonNull List<TunedInfo> tunedInfos) { } } @@ -969,7 +969,7 @@ public final class TvInputManager { }); } - public void onCurrentTunedInfosUpdated(final List<TunedInfo> currentTunedInfos) { + public void postCurrentTunedInfosUpdated(final List<TunedInfo> currentTunedInfos) { mHandler.post(new Runnable() { @Override public void run() { @@ -1286,7 +1286,7 @@ public final class TvInputManager { public void onCurrentTunedInfosUpdated(List<TunedInfo> currentTunedInfos) { synchronized (mLock) { for (TvInputCallbackRecord record : mCallbackRecords) { - record.onCurrentTunedInfosUpdated(currentTunedInfos); + record.postCurrentTunedInfosUpdated(currentTunedInfos); } } } @@ -1988,6 +1988,7 @@ public final class TvInputManager { * {@link TunedInfo#getChannelUri()} returns {@code null}. * @hide */ + @SystemApi @RequiresPermission("com.android.providers.tv.permission.ACCESS_WATCHED_PROGRAMS") @NonNull public List<TunedInfo> getCurrentTunedInfos() { diff --git a/media/java/android/media/tv/tuner/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java index 27a49a75c6d0..1881e38a5010 100644 --- a/media/java/android/media/tv/tuner/Tuner.java +++ b/media/java/android/media/tv/tuner/Tuner.java @@ -122,10 +122,25 @@ public class Tuner implements AutoCloseable { android.hardware.tv.tuner.V1_1.Constants.Constant .INVALID_MMTP_RECORD_EVENT_MPT_SEQUENCE_NUM; /** + * Invalid local transport stream id. + * + * <p>Returned by {@link #linkFrontendToCiCam(int)} when the requested failed + * or the hal implementation does not support the operation. + * + * @see #linkFrontendToCiCam(int) + */ + public static final int INVALID_LTS_ID = + android.hardware.tv.tuner.V1_1.Constants.Constant.INVALID_LTS_ID; + /** * Invalid 64-bit filter ID. */ public static final long INVALID_FILTER_ID_64BIT = android.hardware.tv.tuner.V1_1.Constants.Constant64Bit.INVALID_FILTER_ID_64BIT; + /** + * Invalid frequency that is used as the default frontend frequency setting. + */ + public static final int INVALID_FRONTEND_SETTING_FREQUENCY = + android.hardware.tv.tuner.V1_1.Constants.Constant.INVALID_FRONTEND_SETTING_FREQUENCY; /** @hide */ @IntDef(prefix = "SCAN_TYPE_", value = {SCAN_TYPE_UNDEFINED, SCAN_TYPE_AUTO, SCAN_TYPE_BLIND}) @@ -475,7 +490,9 @@ public class Tuner implements AutoCloseable { private native Integer nativeGetAvSyncHwId(Filter filter); private native Long nativeGetAvSyncTime(int avSyncId); private native int nativeConnectCiCam(int ciCamId); + private native int nativeLinkCiCam(int ciCamId); private native int nativeDisconnectCiCam(); + private native int nativeUnlinkCiCam(int ciCamId); private native FrontendInfo nativeGetFrontendInfo(int id); private native Filter nativeOpenFilter(int type, int subType, long bufferSize); private native TimeFilter nativeOpenTimeFilter(); @@ -606,6 +623,10 @@ public class Tuner implements AutoCloseable { * OnTuneEventListener#SIGNAL_NO_SIGNAL} events sent to the {@link OnTuneEventListener} * specified in {@link #setOnTuneEventListener(Executor, OnTuneEventListener)}. * + * <p>Tuning with {@link android.media.tv.tuner.frontend.DtmbFrontendSettings} is only + * supported in Tuner 1.1 or higher version. Unsupported version will cause no-op. Use {@link + * TunerVersionChecker.getTunerVersion()} to get the version information. + * * @param settings Signal delivery information the frontend uses to * search and lock the signal. * @return result status of tune operation. @@ -616,6 +637,12 @@ public class Tuner implements AutoCloseable { public int tune(@NonNull FrontendSettings settings) { Log.d(TAG, "Tune to " + settings.getFrequency()); mFrontendType = settings.getType(); + if (mFrontendType == FrontendSettings.TYPE_DTMB) { + if (!TunerVersionChecker.checkHigherOrEqualVersionTo( + TunerVersionChecker.TUNER_VERSION_1_1, "Tuner with DTMB Frontend")) { + return RESULT_UNAVAILABLE; + } + } if (checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND)) { mFrontendInfo = null; Log.d(TAG, "Write Stats Log for tuning."); @@ -645,6 +672,10 @@ public class Tuner implements AutoCloseable { * * <p>Details for channels found are returned via {@link ScanCallback}. * + * <p>Scanning with {@link android.media.tv.tuner.frontend.DtmbFrontendSettings} is only + * supported in Tuner 1.1 or higher version. Unsupported version will cause no-op. Use {@link + * TunerVersionChecker.getTunerVersion()} to get the version information. + * * @param settings A {@link FrontendSettings} to configure the frontend. * @param scanType The scan type. * @throws SecurityException if the caller does not have appropriate permissions. @@ -660,6 +691,12 @@ public class Tuner implements AutoCloseable { + "started."); } mFrontendType = settings.getType(); + if (mFrontendType == FrontendSettings.TYPE_DTMB) { + if (!TunerVersionChecker.checkHigherOrEqualVersionTo( + TunerVersionChecker.TUNER_VERSION_1_1, "Scan with DTMB Frontend")) { + return RESULT_UNAVAILABLE; + } + } if (checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND)) { mScanCallback = scanCallback; mScanCallbackExecutor = executor; @@ -738,7 +775,8 @@ public class Tuner implements AutoCloseable { * * <p>This retrieve the statuses of the frontend for given status types. * - * @param statusTypes an array of status types which the caller requests. + * @param statusTypes an array of status types which the caller requests. Any types that are not + * in {@link FrontendInfo.getStatusCapabilities()} would be ignored. * @return statuses which response the caller's requests. {@code null} if the operation failed. */ @Nullable @@ -798,6 +836,33 @@ public class Tuner implements AutoCloseable { } /** + * Link Conditional Access Modules (CAM) Frontend to support Common Interface (CI) by-pass mode. + * + * <p>It is used by the client to link CI-CAM to a Frontend. CI by-pass mode requires that + * the CICAM also receives the TS concurrently from the frontend when the Demux is receiving + * the TS directly from the frontend. + * + * <p>Use {@link #unlinkFrontendToCicam(int)} to disconnect. + * + * <p>This API is only supported by Tuner HAL 1.1 or higher. Unsupported version would cause + * no-op and return {@link INVALID_LTS_ID}. Use {@link TunerVersionChecker.getTunerVersion()} to + * check the version. + * + * @param ciCamId specify CI-CAM Id to link. + * @return Local transport stream id when connection is successfully established. Failed + * operation returns {@link INVALID_LTS_ID}. + */ + public int linkFrontendToCiCam(int ciCamId) { + if (TunerVersionChecker.checkHigherOrEqualVersionTo(TunerVersionChecker.TUNER_VERSION_1_1, + "linkFrontendToCiCam")) { + if (checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND)) { + return nativeLinkCiCam(ciCamId); + } + } + return INVALID_LTS_ID; + } + + /** * Disconnects Conditional Access Modules (CAM) * * <p>The demux will use the output from the frontend as the input after this call. @@ -813,6 +878,28 @@ public class Tuner implements AutoCloseable { } /** + * Unlink Conditional Access Modules (CAM) Frontend. + * + * <p>It is used by the client to unlink CI-CAM to a Frontend. + * + * <p>This API is only supported by Tuner HAL 1.1 or higher. Unsupported version would cause + * no-op. Use {@link TunerVersionChecker.getTunerVersion()} to check the version. + * + * @param ciCamId specify CI-CAM Id to unlink. + * @return result status of the operation. + */ + @Result + public int unlinkFrontendToCiCam(int ciCamId) { + if (TunerVersionChecker.checkHigherOrEqualVersionTo(TunerVersionChecker.TUNER_VERSION_1_1, + "unlinkFrontendToCiCam")) { + if (checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND)) { + return nativeUnlinkCiCam(ciCamId); + } + } + return RESULT_UNAVAILABLE; + } + + /** * Gets the frontend information. * * @return The frontend information. {@code null} if the operation failed. @@ -961,6 +1048,20 @@ public class Tuner implements AutoCloseable { } } + private void onModulationReported(int modulation) { + if (mScanCallbackExecutor != null && mScanCallback != null) { + mScanCallbackExecutor.execute( + () -> mScanCallback.onModulationReported(modulation)); + } + } + + private void onPriorityReported(boolean isHighPriority) { + if (mScanCallbackExecutor != null && mScanCallback != null) { + mScanCallbackExecutor.execute( + () -> mScanCallback.onPriorityReported(isHighPriority)); + } + } + /** * Opens a filter object based on the given types and buffer size. * diff --git a/media/java/android/media/tv/tuner/TunerVersionChecker.java b/media/java/android/media/tv/tuner/TunerVersionChecker.java index 739f87dd711d..b40ba1e763c1 100644 --- a/media/java/android/media/tv/tuner/TunerVersionChecker.java +++ b/media/java/android/media/tv/tuner/TunerVersionChecker.java @@ -31,7 +31,6 @@ import java.lang.annotation.RetentionPolicy; * * @hide */ -@TestApi @SystemApi public final class TunerVersionChecker { private static final String TAG = "TunerVersionChecker"; diff --git a/media/java/android/media/tv/tuner/dvr/DvrPlayback.java b/media/java/android/media/tv/tuner/dvr/DvrPlayback.java index bb00bb3b8d56..597278b0ea3e 100644 --- a/media/java/android/media/tv/tuner/dvr/DvrPlayback.java +++ b/media/java/android/media/tv/tuner/dvr/DvrPlayback.java @@ -123,13 +123,14 @@ public class DvrPlayback implements AutoCloseable { /** * Attaches a filter to DVR interface for playback. * - * <p>This method will be deprecated. Now it's a no-op. - * <p>Filters opened by {@link Tuner#openFilter} are used for DVR playback. + * @deprecated attaching filters is not valid in Dvr Playback use case. This API is a no-op. + * Filters opened by {@link Tuner#openFilter} are used for DVR playback. * * @param filter the filter to be attached. * @return result status of the operation. */ @Result + @Deprecated public int attachFilter(@NonNull Filter filter) { // no-op return Tuner.RESULT_UNAVAILABLE; @@ -138,13 +139,14 @@ public class DvrPlayback implements AutoCloseable { /** * Detaches a filter from DVR interface. * - * <p>This method will be deprecated. Now it's a no-op. - * <p>Filters opened by {@link Tuner#openFilter} are used for DVR playback. + * @deprecated detaching filters is not valid in Dvr Playback use case. This API is a no-op. + * Filters opened by {@link Tuner#openFilter} are used for DVR playback. * * @param filter the filter to be detached. * @return result status of the operation. */ @Result + @Deprecated public int detachFilter(@NonNull Filter filter) { // no-op return Tuner.RESULT_UNAVAILABLE; diff --git a/media/java/android/media/tv/tuner/filter/AvSettings.java b/media/java/android/media/tv/tuner/filter/AvSettings.java index e9b3660ab12f..5d9d53174b3e 100644 --- a/media/java/android/media/tv/tuner/filter/AvSettings.java +++ b/media/java/android/media/tv/tuner/filter/AvSettings.java @@ -16,9 +16,15 @@ package android.media.tv.tuner.filter; +import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.SystemApi; +import android.hardware.tv.tuner.V1_1.Constants; import android.media.tv.tuner.TunerUtils; +import android.media.tv.tuner.TunerVersionChecker; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; /** * Filter Settings for a Video and Audio. @@ -27,15 +33,160 @@ import android.media.tv.tuner.TunerUtils; */ @SystemApi public class AvSettings extends Settings { + /** @hide */ + @IntDef(prefix = "VIDEO_STREAM_TYPE_", + value = {VIDEO_STREAM_TYPE_UNDEFINED, VIDEO_STREAM_TYPE_RESERVED, + VIDEO_STREAM_TYPE_MPEG1, VIDEO_STREAM_TYPE_MPEG2, + VIDEO_STREAM_TYPE_MPEG4P2, VIDEO_STREAM_TYPE_AVC, VIDEO_STREAM_TYPE_HEVC, + VIDEO_STREAM_TYPE_VC1, VIDEO_STREAM_TYPE_VP8, VIDEO_STREAM_TYPE_VP9, + VIDEO_STREAM_TYPE_AV1, VIDEO_STREAM_TYPE_AVS, VIDEO_STREAM_TYPE_AVS2}) + @Retention(RetentionPolicy.SOURCE) + public @interface VideoStreamType {} + + /* + * Undefined Video stream type + */ + public static final int VIDEO_STREAM_TYPE_UNDEFINED = Constants.VideoStreamType.UNDEFINED; + /* + * ITU-T | ISO/IEC Reserved + */ + public static final int VIDEO_STREAM_TYPE_RESERVED = Constants.VideoStreamType.RESERVED; + /* + * ISO/IEC 11172 + */ + public static final int VIDEO_STREAM_TYPE_MPEG1 = Constants.VideoStreamType.MPEG1; + /* + * ITU-T Rec.H.262 and ISO/IEC 13818-2 + */ + public static final int VIDEO_STREAM_TYPE_MPEG2 = Constants.VideoStreamType.MPEG2; + /* + * ISO/IEC 14496-2 (MPEG-4 H.263 based video) + */ + public static final int VIDEO_STREAM_TYPE_MPEG4P2 = Constants.VideoStreamType.MPEG4P2; + /* + * ITU-T Rec.H.264 and ISO/IEC 14496-10 + */ + public static final int VIDEO_STREAM_TYPE_AVC = Constants.VideoStreamType.AVC; + /* + * ITU-T Rec. H.265 and ISO/IEC 23008-2 + */ + public static final int VIDEO_STREAM_TYPE_HEVC = Constants.VideoStreamType.HEVC; + /* + * Microsoft VC.1 + */ + public static final int VIDEO_STREAM_TYPE_VC1 = Constants.VideoStreamType.VC1; + /* + * Google VP8 + */ + public static final int VIDEO_STREAM_TYPE_VP8 = Constants.VideoStreamType.VP8; + /* + * Google VP9 + */ + public static final int VIDEO_STREAM_TYPE_VP9 = Constants.VideoStreamType.VP9; + /* + * AOMedia Video 1 + */ + public static final int VIDEO_STREAM_TYPE_AV1 = Constants.VideoStreamType.AV1; + /* + * Chinese Standard + */ + public static final int VIDEO_STREAM_TYPE_AVS = Constants.VideoStreamType.AVS; + /* + * New Chinese Standard + */ + public static final int VIDEO_STREAM_TYPE_AVS2 = Constants.VideoStreamType.AVS2; + + /** @hide */ + @IntDef(prefix = "AUDIO_STREAM_TYPE_", + value = {AUDIO_STREAM_TYPE_UNDEFINED, AUDIO_STREAM_TYPE_PCM, AUDIO_STREAM_TYPE_MP3, + AUDIO_STREAM_TYPE_MPEG1, AUDIO_STREAM_TYPE_MPEG2, AUDIO_STREAM_TYPE_MPEGH, + AUDIO_STREAM_TYPE_AAC, AUDIO_STREAM_TYPE_AC3, AUDIO_STREAM_TYPE_EAC3, + AUDIO_STREAM_TYPE_AC4, AUDIO_STREAM_TYPE_DTS, AUDIO_STREAM_TYPE_DTS_HD, + AUDIO_STREAM_TYPE_WMA, AUDIO_STREAM_TYPE_OPUS, AUDIO_STREAM_TYPE_VORBIS, + AUDIO_STREAM_TYPE_DRA}) + @Retention(RetentionPolicy.SOURCE) + public @interface AudioStreamType {} + + /* + * Undefined Audio stream type + */ + public static final int AUDIO_STREAM_TYPE_UNDEFINED = Constants.AudioStreamType.UNDEFINED; + /* + * Uncompressed Audio + */ + public static final int AUDIO_STREAM_TYPE_PCM = Constants.AudioStreamType.PCM; + /* + * MPEG Audio Layer III versions + */ + public static final int AUDIO_STREAM_TYPE_MP3 = Constants.AudioStreamType.MP3; + /* + * ISO/IEC 11172 Audio + */ + public static final int AUDIO_STREAM_TYPE_MPEG1 = Constants.AudioStreamType.MPEG1; + /* + * ISO/IEC 13818-3 + */ + public static final int AUDIO_STREAM_TYPE_MPEG2 = Constants.AudioStreamType.MPEG2; + /* + * ISO/IEC 23008-3 (MPEG-H Part 3) + */ + public static final int AUDIO_STREAM_TYPE_MPEGH = Constants.AudioStreamType.MPEGH; + /* + * ISO/IEC 14496-3 + */ + public static final int AUDIO_STREAM_TYPE_AAC = Constants.AudioStreamType.AAC; + /* + * Dolby Digital + */ + public static final int AUDIO_STREAM_TYPE_AC3 = Constants.AudioStreamType.AC3; + /* + * Dolby Digital Plus + */ + public static final int AUDIO_STREAM_TYPE_EAC3 = Constants.AudioStreamType.EAC3; + /* + * Dolby AC-4 + */ + public static final int AUDIO_STREAM_TYPE_AC4 = Constants.AudioStreamType.AC4; + /* + * Basic DTS + */ + public static final int AUDIO_STREAM_TYPE_DTS = Constants.AudioStreamType.DTS; + /* + * High Resolution DTS + */ + public static final int AUDIO_STREAM_TYPE_DTS_HD = Constants.AudioStreamType.DTS_HD; + /* + * Windows Media Audio + */ + public static final int AUDIO_STREAM_TYPE_WMA = Constants.AudioStreamType.WMA; + /* + * Opus Interactive Audio Codec + */ + public static final int AUDIO_STREAM_TYPE_OPUS = Constants.AudioStreamType.OPUS; + /* + * VORBIS Interactive Audio Codec + */ + public static final int AUDIO_STREAM_TYPE_VORBIS = Constants.AudioStreamType.VORBIS; + /* + * SJ/T 11368-2006 + */ + public static final int AUDIO_STREAM_TYPE_DRA = Constants.AudioStreamType.DRA; + + private final boolean mIsPassthrough; + private int mAudioStreamType = AUDIO_STREAM_TYPE_UNDEFINED; + private int mVideoStreamType = VIDEO_STREAM_TYPE_UNDEFINED; - private AvSettings(int mainType, boolean isAudio, boolean isPassthrough) { + private AvSettings(int mainType, boolean isAudio, boolean isPassthrough, + int audioStreamType, int videoStreamType) { super(TunerUtils.getFilterSubtype( mainType, isAudio ? Filter.SUBTYPE_AUDIO : Filter.SUBTYPE_VIDEO)); mIsPassthrough = isPassthrough; + mAudioStreamType = audioStreamType; + mVideoStreamType = videoStreamType; } /** @@ -46,6 +197,20 @@ public class AvSettings extends Settings { } /** + * Get the Audio Stream Type. + */ + public int getAudioStreamType() { + return mAudioStreamType; + } + + /** + * Get the Video Stream Type. + */ + public int getVideoStreamType() { + return mVideoStreamType; + } + + /** * Creates a builder for {@link AvSettings}. * * @param mainType the filter main type. @@ -63,6 +228,8 @@ public class AvSettings extends Settings { private final int mMainType; private final boolean mIsAudio; private boolean mIsPassthrough; + private int mAudioStreamType = AUDIO_STREAM_TYPE_UNDEFINED; + private int mVideoStreamType = VIDEO_STREAM_TYPE_UNDEFINED; private Builder(int mainType, boolean isAudio) { mMainType = mainType; @@ -79,11 +246,48 @@ public class AvSettings extends Settings { } /** + * Sets the Audio Stream Type. + * + * <p>This API is only supported by Tuner HAL 1.1 or higher. Unsupported version would cause + * no-op. Use {@link TunerVersionChecker.getTunerVersion()} to check the version. + * + * @param audioStreamType the {@link AudioStreamType} to set. + */ + @NonNull + public Builder setAudioStreamType(@AudioStreamType int audioStreamType) { + if (TunerVersionChecker.checkHigherOrEqualVersionTo( + TunerVersionChecker.TUNER_VERSION_1_1, "setAudioStreamType") && mIsAudio) { + mAudioStreamType = audioStreamType; + mVideoStreamType = VIDEO_STREAM_TYPE_UNDEFINED; + } + return this; + } + + /** + * Sets the Video Stream Type. + * + * <p>This API is only supported by Tuner HAL 1.1 or higher. Unsupported version would cause + * no-op. Use {@link TunerVersionChecker.getTunerVersion()} to check the version. + * + * @param videoStreamType the {@link VideoStreamType} to set. + */ + @NonNull + public Builder setVideoStreamType(@VideoStreamType int videoStreamType) { + if (TunerVersionChecker.checkHigherOrEqualVersionTo( + TunerVersionChecker.TUNER_VERSION_1_1, "setVideoStreamType") && !mIsAudio) { + mVideoStreamType = videoStreamType; + mAudioStreamType = AUDIO_STREAM_TYPE_UNDEFINED; + } + return this; + } + + /** * Builds a {@link AvSettings} object. */ @NonNull public AvSettings build() { - return new AvSettings(mMainType, mIsAudio, mIsPassthrough); + return new AvSettings(mMainType, mIsAudio, mIsPassthrough, + mAudioStreamType, mVideoStreamType); } } } diff --git a/media/java/android/media/tv/tuner/filter/IpFilterConfiguration.java b/media/java/android/media/tv/tuner/filter/IpFilterConfiguration.java index f54b686304c1..62d55f54cb26 100644 --- a/media/java/android/media/tv/tuner/filter/IpFilterConfiguration.java +++ b/media/java/android/media/tv/tuner/filter/IpFilterConfiguration.java @@ -16,10 +16,12 @@ package android.media.tv.tuner.filter; +import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.Size; import android.annotation.SystemApi; +import android.media.tv.tuner.TunerVersionChecker; /** * Filter configuration for a IP filter. @@ -28,20 +30,28 @@ import android.annotation.SystemApi; */ @SystemApi public final class IpFilterConfiguration extends FilterConfiguration { + /** + * Undefined filter type. + */ + public static final int INVALID_IP_FILTER_CONTEXT_ID = + android.hardware.tv.tuner.V1_1.Constants.Constant.INVALID_IP_FILTER_CONTEXT_ID; + private final byte[] mSrcIpAddress; private final byte[] mDstIpAddress; private final int mSrcPort; private final int mDstPort; private final boolean mPassthrough; + private final int mIpFilterContextId; private IpFilterConfiguration(Settings settings, byte[] srcAddr, byte[] dstAddr, int srcPort, - int dstPort, boolean passthrough) { + int dstPort, boolean passthrough, int ipCid) { super(settings); mSrcIpAddress = srcAddr; mDstIpAddress = dstAddr; mSrcPort = srcPort; mDstPort = dstPort; mPassthrough = passthrough; + mIpFilterContextId = ipCid; } @Override @@ -86,6 +96,16 @@ public final class IpFilterConfiguration extends FilterConfiguration { public boolean isPassthrough() { return mPassthrough; } + /** + * Gets the ip filter context id. Default value is {@link #INVALID_IP_FILTER_CONTEXT_ID}. + * + * <p>This API is only supported by Tuner HAL 1.1 or higher. Unsupported version would return + * default value. Use {@link TunerVersionChecker.getTunerVersion()} to check the version. + */ + @IntRange(from = 0, to = 0xefff) + public int getIpFilterContextId() { + return mIpFilterContextId; + } /** * Creates a builder for {@link IpFilterConfiguration}. @@ -105,6 +125,7 @@ public final class IpFilterConfiguration extends FilterConfiguration { private int mDstPort = 0; private boolean mPassthrough = false; private Settings mSettings; + private int mIpCid = INVALID_IP_FILTER_CONTEXT_ID; private Builder() { } @@ -170,6 +191,21 @@ public final class IpFilterConfiguration extends FilterConfiguration { } /** + * Sets the ip filter context id. Default value is {@link #INVALID_IP_FILTER_CONTEXT_ID}. + * + * <p>This API is only supported by Tuner HAL 1.1 or higher. Unsupported version would cause + * no-op. Use {@link TunerVersionChecker.getTunerVersion()} to check the version. + */ + @NonNull + public Builder setIpFilterContextId(int ipContextId) { + if (TunerVersionChecker.checkHigherOrEqualVersionTo( + TunerVersionChecker.TUNER_VERSION_1_1, "setIpFilterContextId")) { + mIpCid = ipContextId; + } + return this; + } + + /** * Builds a {@link IpFilterConfiguration} object. */ @NonNull @@ -180,8 +216,8 @@ public final class IpFilterConfiguration extends FilterConfiguration { "The lengths of src and dst IP address must be 4 or 16 and must be the same." + "srcLength=" + ipAddrLength + ", dstLength=" + mDstIpAddress.length); } - return new IpFilterConfiguration( - mSettings, mSrcIpAddress, mDstIpAddress, mSrcPort, mDstPort, mPassthrough); + return new IpFilterConfiguration(mSettings, mSrcIpAddress, mDstIpAddress, mSrcPort, + mDstPort, mPassthrough, mIpCid); } } } diff --git a/media/java/android/media/tv/tuner/filter/MediaEvent.java b/media/java/android/media/tv/tuner/filter/MediaEvent.java index 57a04fd70a0b..91be5c38d693 100644 --- a/media/java/android/media/tv/tuner/filter/MediaEvent.java +++ b/media/java/android/media/tv/tuner/filter/MediaEvent.java @@ -187,7 +187,6 @@ public class MediaEvent extends FilterEvent { /** * Releases the MediaEvent object. - * @hide */ public void release() { synchronized (mLock) { diff --git a/media/java/android/media/tv/tuner/frontend/AnalogFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/AnalogFrontendSettings.java index 1d36da34429e..c6a5bb045a1d 100644 --- a/media/java/android/media/tv/tuner/frontend/AnalogFrontendSettings.java +++ b/media/java/android/media/tv/tuner/frontend/AnalogFrontendSettings.java @@ -21,6 +21,7 @@ import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.SystemApi; import android.hardware.tv.tuner.V1_0.Constants; +import android.media.tv.tuner.TunerVersionChecker; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -164,9 +165,32 @@ public class AnalogFrontendSettings extends FrontendSettings { */ public static final int SIF_L_PRIME = Constants.FrontendAnalogSifStandard.L_PRIME; + /** @hide */ + @IntDef(prefix = "AFT_FLAG_", + value = {AFT_FLAG_UNDEFINED, AFT_FLAG_TRUE, AFT_FLAG_FALSE}) + @Retention(RetentionPolicy.SOURCE) + public @interface AftFlag {} + + /** + * Aft flag is not defined. + */ + public static final int AFT_FLAG_UNDEFINED = + android.hardware.tv.tuner.V1_1.Constants.FrontendAnalogAftFlag.UNDEFINED; + /** + * Aft flag is set true. + */ + public static final int AFT_FLAG_TRUE = + android.hardware.tv.tuner.V1_1.Constants.FrontendAnalogAftFlag.AFT_TRUE; + /** + * Aft flag is not set. + */ + public static final int AFT_FLAG_FALSE = + android.hardware.tv.tuner.V1_1.Constants.FrontendAnalogAftFlag.AFT_FALSE; + private final int mSignalType; private final int mSifStandard; + private final int mAftFlag; @Override public int getType() { @@ -191,6 +215,14 @@ public class AnalogFrontendSettings extends FrontendSettings { } /** + * Gets AFT flag. + */ + @AftFlag + public int getAftFlag() { + return mAftFlag; + } + + /** * Creates a builder for {@link AnalogFrontendSettings}. */ @NonNull @@ -198,10 +230,11 @@ public class AnalogFrontendSettings extends FrontendSettings { return new Builder(); } - private AnalogFrontendSettings(int frequency, int signalType, int sifStandard) { + private AnalogFrontendSettings(int frequency, int signalType, int sifStandard, int aftFlag) { super(frequency); mSignalType = signalType; mSifStandard = sifStandard; + mAftFlag = aftFlag; } /** @@ -211,6 +244,7 @@ public class AnalogFrontendSettings extends FrontendSettings { private int mFrequency = 0; private int mSignalType = SIGNAL_TYPE_UNDEFINED; private int mSifStandard = SIF_UNDEFINED; + private int mAftFlag = AFT_FLAG_UNDEFINED; private Builder() {} @@ -227,6 +261,24 @@ public class AnalogFrontendSettings extends FrontendSettings { } /** + * Set Aft flag. + * + * <p>This API is only supported by Tuner HAL 1.1 or higher. Unsupported version would cause + * no-op. Use {@link TunerVersionChecker.getTunerVersion()} to check the version. + * + * @param aftFlag the value to set the aft flag. The default value is + * {@link #AFT_FLAG_UNDEFINED}. + */ + @NonNull + public Builder setAftFlag(@AftFlag int aftFlag) { + if (TunerVersionChecker.checkHigherOrEqualVersionTo( + TunerVersionChecker.TUNER_VERSION_1_1, "setAftFlag")) { + mAftFlag = aftFlag; + } + return this; + } + + /** * Sets analog signal type. * * <p>Default value is {@link #SIGNAL_TYPE_UNDEFINED}. @@ -253,7 +305,7 @@ public class AnalogFrontendSettings extends FrontendSettings { */ @NonNull public AnalogFrontendSettings build() { - return new AnalogFrontendSettings(mFrequency, mSignalType, mSifStandard); + return new AnalogFrontendSettings(mFrequency, mSignalType, mSifStandard, mAftFlag); } } } diff --git a/media/java/android/media/tv/tuner/frontend/Atsc3FrontendSettings.java b/media/java/android/media/tv/tuner/frontend/Atsc3FrontendSettings.java index f9eabc5b50e5..ed1ce2d6a566 100644 --- a/media/java/android/media/tv/tuner/frontend/Atsc3FrontendSettings.java +++ b/media/java/android/media/tv/tuner/frontend/Atsc3FrontendSettings.java @@ -377,8 +377,8 @@ public class Atsc3FrontendSettings extends FrontendSettings { */ @NonNull public Atsc3FrontendSettings build() { - return new Atsc3FrontendSettings( - mFrequency, mBandwidth, mDemodOutputFormat, mPlpSettings); + return new Atsc3FrontendSettings(mFrequency, mBandwidth, mDemodOutputFormat, + mPlpSettings); } } diff --git a/media/java/android/media/tv/tuner/frontend/DtmbFrontendCapabilities.java b/media/java/android/media/tv/tuner/frontend/DtmbFrontendCapabilities.java new file mode 100644 index 000000000000..9fc3a234876b --- /dev/null +++ b/media/java/android/media/tv/tuner/frontend/DtmbFrontendCapabilities.java @@ -0,0 +1,114 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media.tv.tuner.frontend; + +import android.annotation.SystemApi; + +/** + * DTMB Capabilities. + * + * <p>DTMB Frontend is only supported in Tuner HAL 1.1 or higher. + * @hide + */ +@SystemApi +public class DtmbFrontendCapabilities extends FrontendCapabilities { + private final int mModulationCap; + private final int mTransmissionModeCap; + private final int mGuardIntervalCap; + private final int mTimeInterleaveModeCap; + private final int mCodeRateCap; + private final int mBandwidthCap; + + private DtmbFrontendCapabilities(int modulationCap, int transmissionModeCap, + int guardIntervalCap, int timeInterleaveModeCap, int codeRateCap, int bandwidthCap) { + mModulationCap = modulationCap; + mTransmissionModeCap = transmissionModeCap; + mGuardIntervalCap = guardIntervalCap; + mTimeInterleaveModeCap = timeInterleaveModeCap; + mCodeRateCap = codeRateCap; + mBandwidthCap = bandwidthCap; + } + + /** + * Gets modulation capability. + * + * @return full modulation capabilies. If the caps bitwise AND with any value from + * bit masks {@link DtmbFrontendSettings.Modulation} is true, then that modulation is supported. + */ + @DtmbFrontendSettings.Modulation + public int getModulationCapability() { + return mModulationCap; + } + + /** + * Gets Transmission Mode capability. + * + * @return full Transmission Mode capabilies. If the caps bitwise AND with any value from + * bit masks {@link DtmbFrontendSettings.TransmissionMode} is true, then that transmission mode + * is supported. + */ + @DtmbFrontendSettings.TransmissionMode + public int getTransmissionModeCapability() { + return mTransmissionModeCap; + } + + /** + * Gets Guard Interval capability. + * + * @return full Guard Interval capabilies. If the caps bitwise AND with any value from + * bit masks {@link DtmbFrontendSettings.GuardInterval} is true, then that Guard Interval is + * supported. + */ + @DtmbFrontendSettings.GuardInterval + public int getGuardIntervalCapability() { + return mGuardIntervalCap; + } + + /** + * Gets Time Interleave Mode capability. + * + * @return full Time Interleave Mode capabilies. If the caps bitwise AND with any value from + * bit masks {@link DtmbFrontendSettings.TimeInterleaveMode} is true, then that Time Interleave + * Mode is supported. + */ + @DtmbFrontendSettings.TimeInterleaveMode + public int getTimeInterleaveModeCapability() { + return mTimeInterleaveModeCap; + } + + /** + * Gets Code Rate capability. + * + * @return full Code Rate capabilies. If the caps bitwise AND with any value from + * bit masks {@link DtmbFrontendSettings.CodeRate} is true, then that Code Rate is supported. + */ + @DtmbFrontendSettings.CodeRate + public int getCodeRateCapability() { + return mCodeRateCap; + } + + /** + * Gets Bandwidth capability. + * + * @return full Bandwidth capabilies. If the caps bitwise AND with any value from + * bit masks {@link DtmbFrontendSettings.Bandwidth} is true, then that Bandwidth is supported. + */ + @DtmbFrontendSettings.Bandwidth + public int getBandwidthCapability() { + return mBandwidthCap; + } +} diff --git a/media/java/android/media/tv/tuner/frontend/DtmbFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/DtmbFrontendSettings.java new file mode 100644 index 000000000000..2c3fe6a2583c --- /dev/null +++ b/media/java/android/media/tv/tuner/frontend/DtmbFrontendSettings.java @@ -0,0 +1,441 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.media.tv.tuner.frontend; + +import android.annotation.IntDef; +import android.annotation.IntRange; +import android.annotation.NonNull; +import android.annotation.SuppressLint; +import android.annotation.SystemApi; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Frontend settings for DTMB. + * + * <p>DTMB Frontend is only supported in Tuner HAL 1.1 or higher. Use {@link + * android.media.tv.tuner.TunerVersionChecker.getTunerVersion()} to get the version information. + * + * @hide + */ +@SystemApi +public class DtmbFrontendSettings extends FrontendSettings { + + /** @hide */ + @IntDef(flag = true, + prefix = "BANDWIDTH_", + value = {BANDWIDTH_UNDEFINED, BANDWIDTH_AUTO, BANDWIDTH_6MHZ, BANDWIDTH_8MHZ}) + @Retention(RetentionPolicy.SOURCE) + public @interface Bandwidth {} + + /** + * Bandwidth not defined. + */ + public static final int BANDWIDTH_UNDEFINED = + android.hardware.tv.tuner.V1_1.Constants.FrontendDtmbBandwidth.UNDEFINED; + /** + * Hardware is able to detect and set bandwidth automatically + */ + public static final int BANDWIDTH_AUTO = + android.hardware.tv.tuner.V1_1.Constants.FrontendDtmbBandwidth.AUTO; + /** + * 6 MHz bandwidth. + */ + public static final int BANDWIDTH_6MHZ = + android.hardware.tv.tuner.V1_1.Constants.FrontendDtmbBandwidth.BANDWIDTH_6MHZ; + /** + * 8 MHz bandwidth. + */ + public static final int BANDWIDTH_8MHZ = + android.hardware.tv.tuner.V1_1.Constants.FrontendDtmbBandwidth.BANDWIDTH_8MHZ; + + + /** @hide */ + @IntDef(flag = true, + prefix = "TIME_INTERLEAVE_MODE_", + value = {TIME_INTERLEAVE_MODE_UNDEFINED, TIME_INTERLEAVE_MODE_AUTO, + TIME_INTERLEAVE_MODE_TIMER_INT_240, TIME_INTERLEAVE_MODE_TIMER_INT_720}) + @Retention(RetentionPolicy.SOURCE) + public @interface TimeInterleaveMode {} + + /** + * Time Interleave Mode undefined. + */ + public static final int TIME_INTERLEAVE_MODE_UNDEFINED = + android.hardware.tv.tuner.V1_1.Constants.FrontendDtmbTimeInterleaveMode.UNDEFINED; + /** + * Hardware is able to detect and set time interleave mode automatically + */ + public static final int TIME_INTERLEAVE_MODE_AUTO = + android.hardware.tv.tuner.V1_1.Constants.FrontendDtmbTimeInterleaveMode.AUTO; + /** + * Time Interleave Mode timer int 240. + */ + public static final int TIME_INTERLEAVE_MODE_TIMER_INT_240 = + android.hardware.tv.tuner.V1_1.Constants.FrontendDtmbTimeInterleaveMode.TIMER_INT_240; + /** + * Time Interleave Mode timer int 720. + */ + public static final int TIME_INTERLEAVE_MODE_TIMER_INT_720 = + android.hardware.tv.tuner.V1_1.Constants.FrontendDtmbTimeInterleaveMode.TIMER_INT_720; + + + /** @hide */ + @IntDef(flag = true, + prefix = "GUARD_INTERVAL_", + value = {GUARD_INTERVAL_UNDEFINED, GUARD_INTERVAL_AUTO, + GUARD_INTERVAL_PN_420_VARIOUS, GUARD_INTERVAL_PN_595_CONST, + GUARD_INTERVAL_PN_945_VARIOUS, GUARD_INTERVAL_PN_420_CONST, + GUARD_INTERVAL_PN_945_CONST, GUARD_INTERVAL_PN_RESERVED}) + @Retention(RetentionPolicy.SOURCE) + public @interface GuardInterval {} + + /** + * Guard Interval undefined. + */ + public static final int GUARD_INTERVAL_UNDEFINED = + android.hardware.tv.tuner.V1_1.Constants.FrontendDtmbGuardInterval.UNDEFINED; + /** + * Hardware is able to detect and set Guard Interval automatically. + */ + public static final int GUARD_INTERVAL_AUTO = + android.hardware.tv.tuner.V1_1.Constants.FrontendDtmbGuardInterval.AUTO; + /** + * PN_420_VARIOUS Guard Interval. + */ + public static final int GUARD_INTERVAL_PN_420_VARIOUS = + android.hardware.tv.tuner.V1_1.Constants.FrontendDtmbGuardInterval.PN_420_VARIOUS; + /** + * PN_595_CONST Guard Interval. + */ + public static final int GUARD_INTERVAL_PN_595_CONST = + android.hardware.tv.tuner.V1_1.Constants.FrontendDtmbGuardInterval.PN_595_CONST; + /** + * PN_945_VARIOUS Guard Interval. + */ + public static final int GUARD_INTERVAL_PN_945_VARIOUS = + android.hardware.tv.tuner.V1_1.Constants.FrontendDtmbGuardInterval.PN_945_VARIOUS; + /** + * PN_420_CONST Guard Interval. + */ + public static final int GUARD_INTERVAL_PN_420_CONST = + android.hardware.tv.tuner.V1_1.Constants.FrontendDtmbGuardInterval.PN_420_CONST; + /** + * PN_945_CONST Guard Interval. + */ + public static final int GUARD_INTERVAL_PN_945_CONST = + android.hardware.tv.tuner.V1_1.Constants.FrontendDtmbGuardInterval.PN_945_CONST; + /** + * PN_RESERVED Guard Interval. + */ + public static final int GUARD_INTERVAL_PN_RESERVED = + android.hardware.tv.tuner.V1_1.Constants.FrontendDtmbGuardInterval.PN_RESERVED; + + + /** @hide */ + @IntDef(flag = true, + prefix = "MODULATION_", + value = {MODULATION_CONSTELLATION_UNDEFINED, MODULATION_CONSTELLATION_AUTO, + MODULATION_CONSTELLATION_4QAM, MODULATION_CONSTELLATION_4QAM_NR, + MODULATION_CONSTELLATION_16QAM, MODULATION_CONSTELLATION_32QAM, + MODULATION_CONSTELLATION_64QAM}) + @Retention(RetentionPolicy.SOURCE) + public @interface Modulation {} + + /** + * Constellation not defined. + */ + public static final int MODULATION_CONSTELLATION_UNDEFINED = + android.hardware.tv.tuner.V1_1.Constants.FrontendDtmbModulation.UNDEFINED; + /** + * Hardware is able to detect and set Constellation automatically. + */ + public static final int MODULATION_CONSTELLATION_AUTO = + android.hardware.tv.tuner.V1_1.Constants.FrontendDtmbModulation.AUTO; + /** + * 4QAM Constellation. + */ + public static final int MODULATION_CONSTELLATION_4QAM = + android.hardware.tv.tuner.V1_1.Constants.FrontendDtmbModulation.CONSTELLATION_4QAM; + /** + * 4QAM_NR Constellation. + */ + public static final int MODULATION_CONSTELLATION_4QAM_NR = + android.hardware.tv.tuner.V1_1.Constants.FrontendDtmbModulation.CONSTELLATION_4QAM_NR; + /** + * 16QAM Constellation. + */ + public static final int MODULATION_CONSTELLATION_16QAM = + android.hardware.tv.tuner.V1_1.Constants.FrontendDtmbModulation.CONSTELLATION_16QAM; + /** + * 32QAM Constellation. + */ + public static final int MODULATION_CONSTELLATION_32QAM = + android.hardware.tv.tuner.V1_1.Constants.FrontendDtmbModulation.CONSTELLATION_32QAM; + /** + * 64QAM Constellation. + */ + public static final int MODULATION_CONSTELLATION_64QAM = + android.hardware.tv.tuner.V1_1.Constants.FrontendDtmbModulation.CONSTELLATION_64QAM; + + /** @hide */ + @IntDef(flag = true, + prefix = "CODERATE_", + value = {CODERATE_UNDEFINED, CODERATE_AUTO, CODERATE_2_5, CODERATE_3_5, CODERATE_4_5}) + @Retention(RetentionPolicy.SOURCE) + public @interface CodeRate {} + + /** + * Code rate undefined. + */ + public static final int CODERATE_UNDEFINED = + android.hardware.tv.tuner.V1_1.Constants.FrontendDtmbCodeRate.UNDEFINED; + /** + * Hardware is able to detect and set code rate automatically. + */ + public static final int CODERATE_AUTO = + android.hardware.tv.tuner.V1_1.Constants.FrontendDtmbCodeRate.AUTO; + /** + * 2/5 code rate. + */ + public static final int CODERATE_2_5 = + android.hardware.tv.tuner.V1_1.Constants.FrontendDtmbCodeRate.CODERATE_2_5; + /** + * 3/5 code rate. + */ + public static final int CODERATE_3_5 = + android.hardware.tv.tuner.V1_1.Constants.FrontendDtmbCodeRate.CODERATE_3_5; + /** + * 4/5 code rate. + */ + public static final int CODERATE_4_5 = + android.hardware.tv.tuner.V1_1.Constants.FrontendDtmbCodeRate.CODERATE_4_5; + + /** @hide */ + @IntDef(flag = true, + prefix = "TRANSMISSION_MODE_", + value = {TRANSMISSION_MODE_UNDEFINED, TRANSMISSION_MODE_AUTO, + TRANSMISSION_MODE_C1, TRANSMISSION_MODE_C3780}) + @Retention(RetentionPolicy.SOURCE) + public @interface TransmissionMode {} + + /** + * Transmission Mode undefined. + */ + public static final int TRANSMISSION_MODE_UNDEFINED = + android.hardware.tv.tuner.V1_1.Constants.FrontendDtmbTransmissionMode.UNDEFINED; + /** + * Hardware is able to detect and set Transmission Mode automatically + */ + public static final int TRANSMISSION_MODE_AUTO = + android.hardware.tv.tuner.V1_1.Constants.FrontendDtmbTransmissionMode.AUTO; + /** + * C1 Transmission Mode. + */ + public static final int TRANSMISSION_MODE_C1 = + android.hardware.tv.tuner.V1_1.Constants.FrontendDtmbTransmissionMode.C1; + /** + * C3780 Transmission Mode. + */ + public static final int TRANSMISSION_MODE_C3780 = + android.hardware.tv.tuner.V1_1.Constants.FrontendDtmbTransmissionMode.C3780; + + + private final int mModulation; + private final int mCodeRate; + private final int mTransmissionMode; + private final int mBandwidth; + private final int mGuardInterval; + private final int mTimeInterleaveMode; + + private DtmbFrontendSettings(int frequency, int modulation, int codeRate, int transmissionMode, + int guardInterval, int timeInterleaveMode, int bandwidth) { + super(frequency); + mModulation = modulation; + mCodeRate = codeRate; + mTransmissionMode = transmissionMode; + mGuardInterval = guardInterval; + mTimeInterleaveMode = timeInterleaveMode; + mBandwidth = bandwidth; + } + + /** + * Gets Modulation. + */ + @Modulation + public int getModulation() { + return mModulation; + } + + /** + * Gets Code Rate. + */ + @Modulation + public int getCodeRate() { + return mCodeRate; + } + + /** + * Gets Transmission Mode. + */ + @Modulation + public int getTransmissionMode() { + return mTransmissionMode; + } + + /** + * Gets Bandwidth. + */ + @Modulation + public int getBandwidth() { + return mBandwidth; + } + + /** + * Gets Time Interleave Mode. + */ + @Modulation + public int getTimeInterleaveMode() { + return mTimeInterleaveMode; + } + + + /** + * Gets Guard Interval. + */ + @Modulation + public int getGuardInterval() { + return mGuardInterval; + } + + /** + * Creates a builder for {@link AtscFrontendSettings}. + */ + @NonNull + public static Builder builder() { + return new Builder(); + } + + /** + * Builder for {@link AtscFrontendSettings}. + */ + public static final class Builder { + private int mFrequency = 0; + private int mModulation = MODULATION_CONSTELLATION_UNDEFINED; + private int mCodeRate = CODERATE_UNDEFINED; + private int mTransmissionMode = TRANSMISSION_MODE_UNDEFINED; + private int mBandwidth = BANDWIDTH_UNDEFINED; + private int mTimeInterleaveMode = TIME_INTERLEAVE_MODE_UNDEFINED; + private int mGuardInterval = GUARD_INTERVAL_UNDEFINED; + + private Builder() { + } + + /** + * Sets frequency in Hz. + * + * <p>Default value is 0. + */ + @NonNull + @IntRange(from = 1) + @SuppressLint("MissingGetterMatchingBuilder") + public Builder setFrequency(int frequency) { + mFrequency = frequency; + return this; + } + + /** + * Sets Modulation. + * + * <p>Default value is {@link #MODULATION_CONSTELLATION_UNDEFINED}. + */ + @NonNull + public Builder setModulation(@Modulation int modulation) { + mModulation = modulation; + return this; + } + + /** + * Sets Code Rate. + * + * <p>Default value is {@link #CODERATE_UNDEFINED}. + */ + @NonNull + public Builder setCodeRate(@CodeRate int codeRate) { + mCodeRate = codeRate; + return this; + } + + /** + * Sets Bandwidth. + * + * <p>Default value is {@link #BANDWIDTH_UNDEFINED}. + */ + @NonNull + public Builder setBandwidth(@Bandwidth int bandwidth) { + mBandwidth = bandwidth; + return this; + } + + /** + * Sets Time Interleave Mode. + * + * <p>Default value is {@link #TIME_INTERLEAVE_MODE_UNDEFINED}. + */ + @NonNull + public Builder setTimeInterleaveMode(@TimeInterleaveMode int timeInterleaveMode) { + mTimeInterleaveMode = timeInterleaveMode; + return this; + } + + /** + * Sets Guard Interval. + * + * <p>Default value is {@link #GUARD_INTERVAL_UNDEFINED}. + */ + @NonNull + public Builder setGuardInterval(@GuardInterval int guardInterval) { + mGuardInterval = guardInterval; + return this; + } + /** + * Sets Transmission Mode. + * + * <p>Default value is {@link #TRANSMISSION_MODE_UNDEFINED}. + */ + @NonNull + public Builder setTransmissionMode(@TransmissionMode int transmissionMode) { + mTransmissionMode = transmissionMode; + return this; + } + + /** + * Builds a {@link DtmbFrontendSettings} object. + */ + @NonNull + public DtmbFrontendSettings build() { + return new DtmbFrontendSettings(mFrequency, mModulation, mCodeRate, + mTransmissionMode, mGuardInterval, mTimeInterleaveMode, mBandwidth); + } + } + + @Override + public int getType() { + return FrontendSettings.TYPE_DTMB; + } +} diff --git a/media/java/android/media/tv/tuner/frontend/DvbcFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/DvbcFrontendSettings.java index 271e91e5cbb8..e6968bb4ec90 100644 --- a/media/java/android/media/tv/tuner/frontend/DvbcFrontendSettings.java +++ b/media/java/android/media/tv/tuner/frontend/DvbcFrontendSettings.java @@ -21,6 +21,8 @@ import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.SystemApi; import android.hardware.tv.tuner.V1_0.Constants; +import android.media.tv.tuner.TunerVersionChecker; +import android.media.tv.tuner.frontend.FrontendSettings.FrontendSpectralInversion; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -117,7 +119,11 @@ public class DvbcFrontendSettings extends FrontendSettings { public static final int ANNEX_C = Constants.FrontendDvbcAnnex.C; - /** @hide */ + /** + * @deprecated Use the {@link FrontendSettings#FrontendSpectralInversion} instead. + * @hide + */ + @Deprecated @IntDef(prefix = "SPECTRAL_INVERSION_", value = {SPECTRAL_INVERSION_UNDEFINED, SPECTRAL_INVERSION_NORMAL, SPECTRAL_INVERSION_INVERTED}) @@ -126,20 +132,97 @@ public class DvbcFrontendSettings extends FrontendSettings { /** * Spectral Inversion Type undefined. + * + * @deprecated Use the {@link FrontendSettings#FRONTEND_SPECTRAL_INVERSION_UNDEFINED} instead. */ + @Deprecated public static final int SPECTRAL_INVERSION_UNDEFINED = Constants.FrontendDvbcSpectralInversion.UNDEFINED; /** * Normal Spectral Inversion. + * + * @deprecated Use the {@link FrontendSettings#FRONTEND_SPECTRAL_INVERSION_NORMAL} instead. */ + @Deprecated public static final int SPECTRAL_INVERSION_NORMAL = Constants.FrontendDvbcSpectralInversion.NORMAL; /** * Inverted Spectral Inversion. + * + * @deprecated Use the {@link FrontendSettings#FRONTEND_SPECTRAL_INVERSION_INVERTED} instead. */ + @Deprecated public static final int SPECTRAL_INVERSION_INVERTED = Constants.FrontendDvbcSpectralInversion.INVERTED; + /** @hide */ + @IntDef(flag = true, + prefix = "TIME_INTERLEAVE_MODE_", + value = {TIME_INTERLEAVE_MODE_UNDEFINED, TIME_INTERLEAVE_MODE_AUTO, + TIME_INTERLEAVE_MODE_128_1_0, TIME_INTERLEAVE_MODE_128_1_1, + TIME_INTERLEAVE_MODE_64_2, TIME_INTERLEAVE_MODE_32_4, + TIME_INTERLEAVE_MODE_16_8, TIME_INTERLEAVE_MODE_8_16, + TIME_INTERLEAVE_MODE_128_2, TIME_INTERLEAVE_MODE_128_3, + TIME_INTERLEAVE_MODE_128_4}) + @Retention(RetentionPolicy.SOURCE) + public @interface TimeInterleaveMode {} + + /** + * Time interleave mode undefined. + */ + public static final int TIME_INTERLEAVE_MODE_UNDEFINED = + android.hardware.tv.tuner.V1_1.Constants.FrontendCableTimeInterleaveMode.UNDEFINED; + /** + * Hardware is able to detect and set Time Interleave Mode automatically. + */ + public static final int TIME_INTERLEAVE_MODE_AUTO = + android.hardware.tv.tuner.V1_1.Constants.FrontendCableTimeInterleaveMode.AUTO; + /** + * 128/1/0 Time Interleave Mode. + */ + public static final int TIME_INTERLEAVE_MODE_128_1_0 = android.hardware.tv.tuner.V1_1.Constants + .FrontendCableTimeInterleaveMode.INTERLEAVING_128_1_0; + /** + * 128/1/1 Time Interleave Mode. + */ + public static final int TIME_INTERLEAVE_MODE_128_1_1 = android.hardware.tv.tuner.V1_1.Constants + .FrontendCableTimeInterleaveMode.INTERLEAVING_128_1_1; + /** + * 64/2 Time Interleave Mode. + */ + public static final int TIME_INTERLEAVE_MODE_64_2 = android.hardware.tv.tuner.V1_1.Constants + .FrontendCableTimeInterleaveMode.INTERLEAVING_64_2; + /** + * 32/4 Time Interleave Mode. + */ + public static final int TIME_INTERLEAVE_MODE_32_4 = android.hardware.tv.tuner.V1_1.Constants + .FrontendCableTimeInterleaveMode.INTERLEAVING_32_4; + /** + * 16/8 Time Interleave Mode. + */ + public static final int TIME_INTERLEAVE_MODE_16_8 = android.hardware.tv.tuner.V1_1.Constants + .FrontendCableTimeInterleaveMode.INTERLEAVING_16_8; + /** + * 8/16 Time Interleave Mode. + */ + public static final int TIME_INTERLEAVE_MODE_8_16 = android.hardware.tv.tuner.V1_1.Constants + .FrontendCableTimeInterleaveMode.INTERLEAVING_8_16; + /** + * 128/2 Time Interleave Mode. + */ + public static final int TIME_INTERLEAVE_MODE_128_2 = android.hardware.tv.tuner.V1_1.Constants + .FrontendCableTimeInterleaveMode.INTERLEAVING_128_2; + /** + * 128/3 Time Interleave Mode. + */ + public static final int TIME_INTERLEAVE_MODE_128_3 = android.hardware.tv.tuner.V1_1.Constants + .FrontendCableTimeInterleaveMode.INTERLEAVING_128_3; + /** + * 128/4 Time Interleave Mode. + */ + public static final int TIME_INTERLEAVE_MODE_128_4 = android.hardware.tv.tuner.V1_1.Constants + .FrontendCableTimeInterleaveMode.INTERLEAVING_128_4; + private final int mModulation; private final long mInnerFec; @@ -147,9 +230,11 @@ public class DvbcFrontendSettings extends FrontendSettings { private final int mOuterFec; private final int mAnnex; private final int mSpectralInversion; + // Dvbc time interleave mode is only supported in Tuner 1.1 or higher. + private final int mInterleaveMode; private DvbcFrontendSettings(int frequency, int modulation, long innerFec, int symbolRate, - int outerFec, int annex, int spectralInversion) { + int outerFec, int annex, int spectralInversion, int interleaveMode) { super(frequency); mModulation = modulation; mInnerFec = innerFec; @@ -157,6 +242,7 @@ public class DvbcFrontendSettings extends FrontendSettings { mOuterFec = outerFec; mAnnex = annex; mSpectralInversion = spectralInversion; + mInterleaveMode = interleaveMode; } /** @@ -196,10 +282,17 @@ public class DvbcFrontendSettings extends FrontendSettings { /** * Gets Spectral Inversion. */ - @SpectralInversion + @FrontendSpectralInversion public int getSpectralInversion() { return mSpectralInversion; } + /** + * Gets Time Interleave Mode. + */ + @TimeInterleaveMode + public int getTimeInterleaveMode() { + return mInterleaveMode; + } /** * Creates a builder for {@link DvbcFrontendSettings}. @@ -219,7 +312,8 @@ public class DvbcFrontendSettings extends FrontendSettings { private int mSymbolRate = 0; private int mOuterFec = OUTER_FEC_UNDEFINED; private int mAnnex = ANNEX_UNDEFINED; - private int mSpectralInversion = SPECTRAL_INVERSION_UNDEFINED; + private int mSpectralInversion = FrontendSettings.FRONTEND_SPECTRAL_INVERSION_UNDEFINED; + private int mInterleaveMode = TIME_INTERLEAVE_MODE_UNDEFINED; private Builder() { } @@ -289,13 +383,30 @@ public class DvbcFrontendSettings extends FrontendSettings { /** * Sets Spectral Inversion. * - * <p>Default value is {@link #SPECTRAL_INVERSION_UNDEFINED}. + * <p>Default value is {@link FrontendSettings#FRONTEND_SPECTRAL_INVERSION_UNDEFINED}. */ @NonNull - public Builder setSpectralInversion(@SpectralInversion int spectralInversion) { + public Builder setSpectralInversion(@FrontendSpectralInversion int spectralInversion) { mSpectralInversion = spectralInversion; return this; } + /** + * Set the time interleave mode. + * + * <p>This API is only supported by Tuner HAL 1.1 or higher. Unsupported version would cause + * no-op. Use {@link TunerVersionChecker.getTunerVersion()} to check the version. + * + * @param interleaveMode the value to set as the time interleave mode. Default value is + * {@link #TIME_INTERLEAVE_MODE_UNDEFINED}. + */ + @NonNull + public Builder setTimeInterleaveMode(@TimeInterleaveMode int interleaveMode) { + if (TunerVersionChecker.checkHigherOrEqualVersionTo( + TunerVersionChecker.TUNER_VERSION_1_1, "setTimeInterleaveMode")) { + mInterleaveMode = interleaveMode; + } + return this; + } /** * Builds a {@link DvbcFrontendSettings} object. @@ -303,7 +414,7 @@ public class DvbcFrontendSettings extends FrontendSettings { @NonNull public DvbcFrontendSettings build() { return new DvbcFrontendSettings(mFrequency, mModulation, mInnerFec, mSymbolRate, - mOuterFec, mAnnex, mSpectralInversion); + mOuterFec, mAnnex, mSpectralInversion, mInterleaveMode); } } diff --git a/media/java/android/media/tv/tuner/frontend/DvbsFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/DvbsFrontendSettings.java index 60b070fa1fa8..fadc00475930 100644 --- a/media/java/android/media/tv/tuner/frontend/DvbsFrontendSettings.java +++ b/media/java/android/media/tv/tuner/frontend/DvbsFrontendSettings.java @@ -23,6 +23,8 @@ import android.annotation.Nullable; import android.annotation.SystemApi; import android.hardware.tv.tuner.V1_0.Constants; import android.media.tv.tuner.Tuner; +import android.media.tv.tuner.TunerVersionChecker; + import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -36,6 +38,44 @@ import java.lang.annotation.RetentionPolicy; public class DvbsFrontendSettings extends FrontendSettings { /** @hide */ @IntDef(flag = true, + prefix = "SCAN_TYPE_", + value = {SCAN_TYPE_UNDEFINED, SCAN_TYPE_DIRECT, SCAN_TYPE_DISEQC, + SCAN_TYPE_UNICABLE, SCAN_TYPE_JESS}) + @Retention(RetentionPolicy.SOURCE) + public @interface ScanType {} + + /** + * Dvbs scan type undefined. + */ + public static final int SCAN_TYPE_UNDEFINED = + android.hardware.tv.tuner.V1_1.Constants.FrontendDvbsScanType.UNDEFINED; + + /** + * Dvbs scan type DIRECT. + */ + public static final int SCAN_TYPE_DIRECT = + android.hardware.tv.tuner.V1_1.Constants.FrontendDvbsScanType.DIRECT; + + /** + * Dvbs scan type DISEQC. + */ + public static final int SCAN_TYPE_DISEQC = + android.hardware.tv.tuner.V1_1.Constants.FrontendDvbsScanType.DISEQC; + + /** + * Dvbs scan type UNICABLE. + */ + public static final int SCAN_TYPE_UNICABLE = + android.hardware.tv.tuner.V1_1.Constants.FrontendDvbsScanType.UNICABLE; + + /** + * Dvbs scan type JESS. + */ + public static final int SCAN_TYPE_JESS = + android.hardware.tv.tuner.V1_1.Constants.FrontendDvbsScanType.JESS; + + /** @hide */ + @IntDef(flag = true, prefix = "MODULATION_", value = {MODULATION_UNDEFINED, MODULATION_AUTO, MODULATION_MOD_QPSK, MODULATION_MOD_8PSK, MODULATION_MOD_16QAM, MODULATION_MOD_16PSK, @@ -218,9 +258,14 @@ public class DvbsFrontendSettings extends FrontendSettings { private final int mInputStreamId; private final int mStandard; private final int mVcmMode; + // Dvbs scan type is only supported in Tuner 1.1 or higher. + private final int mScanType; + // isDiseqcRxMessage is only supported in Tuner 1.1 or higher. + private final boolean mIsDiseqcRxMessage; private DvbsFrontendSettings(int frequency, int modulation, DvbsCodeRate codeRate, - int symbolRate, int rolloff, int pilot, int inputStreamId, int standard, int vcm) { + int symbolRate, int rolloff, int pilot, int inputStreamId, int standard, int vcm, + int scanType, boolean isDiseqcRxMessage) { super(frequency); mModulation = modulation; mCodeRate = codeRate; @@ -230,6 +275,8 @@ public class DvbsFrontendSettings extends FrontendSettings { mInputStreamId = inputStreamId; mStandard = standard; mVcmMode = vcm; + mScanType = scanType; + mIsDiseqcRxMessage = isDiseqcRxMessage; } /** @@ -286,6 +333,22 @@ public class DvbsFrontendSettings extends FrontendSettings { public int getVcmMode() { return mVcmMode; } + /** + * Get scan type. + */ + @ScanType + public int getScanType() { + return mScanType; + } + /** + * To receive Diseqc Message or not. Default value is false. + * + * The setter {@link Builder#setDiseqcRxMessage(boolean)} is only supported with Tuner HAL 1.1 + * or higher. + */ + public boolean isDiseqcRxMessage() { + return mIsDiseqcRxMessage; + } /** * Creates a builder for {@link DvbsFrontendSettings}. @@ -308,6 +371,8 @@ public class DvbsFrontendSettings extends FrontendSettings { private int mInputStreamId = Tuner.INVALID_STREAM_ID; private int mStandard = STANDARD_AUTO; private int mVcmMode = VCM_MODE_UNDEFINED; + private int mScanType = SCAN_TYPE_UNDEFINED; + private boolean mIsDiseqcRxMessage = false; private Builder() { } @@ -325,6 +390,39 @@ public class DvbsFrontendSettings extends FrontendSettings { } /** + * Set the scan type. + * + * <p>This API is only supported by Tuner HAL 1.1 or higher. Unsupported version would cause + * no-op. Use {@link TunerVersionChecker.getTunerVersion()} to check the version. + * + * @param scanType the value to set as the scan type. Default value is + * {@link android.media.tv.tuner.frontend.DvbsFrontendSettings#DVBS_SCAN_TYPE_UNDEFINED}. + */ + @NonNull + public Builder setScanType(@ScanType int scanType) { + if (TunerVersionChecker.checkHigherOrEqualVersionTo( + TunerVersionChecker.TUNER_VERSION_1_1, "setScanType")) { + mScanType = scanType; + } + return this; + } + + /** + * Set true to receive Diseqc Message. + * + * <p>This API is only supported by Tuner HAL 1.1 or higher. Unsupported version would cause + * no-op. Use {@link TunerVersionChecker.getTunerVersion()} to check the version. + */ + @NonNull + public Builder setDiseqcRxMessage(boolean isDiseqcRxMessage) { + if (TunerVersionChecker.checkHigherOrEqualVersionTo( + TunerVersionChecker.TUNER_VERSION_1_1, "setDiseqcRxMessage")) { + mIsDiseqcRxMessage = isDiseqcRxMessage; + } + return this; + } + + /** * Sets Modulation. * * <p>Default value is {@link #MODULATION_UNDEFINED}. @@ -411,7 +509,8 @@ public class DvbsFrontendSettings extends FrontendSettings { @NonNull public DvbsFrontendSettings build() { return new DvbsFrontendSettings(mFrequency, mModulation, mCodeRate, mSymbolRate, - mRolloff, mPilot, mInputStreamId, mStandard, mVcmMode); + mRolloff, mPilot, mInputStreamId, mStandard, mVcmMode, mScanType, + mIsDiseqcRxMessage); } } diff --git a/media/java/android/media/tv/tuner/frontend/DvbtFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/DvbtFrontendSettings.java index 5c057de7cfbe..07d179707b98 100644 --- a/media/java/android/media/tv/tuner/frontend/DvbtFrontendSettings.java +++ b/media/java/android/media/tv/tuner/frontend/DvbtFrontendSettings.java @@ -21,6 +21,7 @@ import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.SystemApi; import android.hardware.tv.tuner.V1_0.Constants; +import android.media.tv.tuner.TunerVersionChecker; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -75,8 +76,21 @@ public class DvbtFrontendSettings extends FrontendSettings { * 32K Transmission Mode. */ public static final int TRANSMISSION_MODE_32K = Constants.FrontendDvbtTransmissionMode.MODE_32K; - - + /** + * 8K Transmission Extended Mode. + */ + public static final int TRANSMISSION_MODE_EXTENDED_8K = + android.hardware.tv.tuner.V1_1.Constants.FrontendDvbtTransmissionMode.MODE_8K_E; + /** + * 16K Transmission Extended Mode. + */ + public static final int TRANSMISSION_MODE_EXTENDED_16K = + android.hardware.tv.tuner.V1_1.Constants.FrontendDvbtTransmissionMode.MODE_16K_E; + /** + * 32K Transmission Extended Mode. + */ + public static final int TRANSMISSION_MODE_EXTENDED_32K = + android.hardware.tv.tuner.V1_1.Constants.FrontendDvbtTransmissionMode.MODE_32K_E; /** @hide */ @IntDef(flag = true, @@ -124,8 +138,9 @@ public class DvbtFrontendSettings extends FrontendSettings { @IntDef(flag = true, prefix = "CONSTELLATION_", value = {CONSTELLATION_UNDEFINED, CONSTELLATION_AUTO, CONSTELLATION_QPSK, - CONSTELLATION_16QAM, CONSTELLATION_64QAM, - CONSTELLATION_256QAM}) + CONSTELLATION_16QAM, CONSTELLATION_64QAM, CONSTELLATION_256QAM, + CONSTELLATION_QPSK_R, CONSTELLATION_16QAM_R, CONSTELLATION_64QAM_R, + CONSTELLATION_256QAM_R}) @Retention(RetentionPolicy.SOURCE) public @interface Constellation {} @@ -157,7 +172,30 @@ public class DvbtFrontendSettings extends FrontendSettings { */ public static final int CONSTELLATION_256QAM = Constants.FrontendDvbtConstellation.CONSTELLATION_256QAM; - + /** + * QPSK Rotated Constellation. + */ + public static final int CONSTELLATION_QPSK_R = + android.hardware.tv.tuner.V1_1.Constants.FrontendDvbtConstellation + .CONSTELLATION_QPSK_R; + /** + * 16QAM Rotated Constellation. + */ + public static final int CONSTELLATION_16QAM_R = + android.hardware.tv.tuner.V1_1.Constants.FrontendDvbtConstellation + .CONSTELLATION_16QAM_R; + /** + * 64QAM Rotated Constellation. + */ + public static final int CONSTELLATION_64QAM_R = + android.hardware.tv.tuner.V1_1.Constants.FrontendDvbtConstellation + .CONSTELLATION_64QAM_R; + /** + * 256QAM Rotated Constellation. + */ + public static final int CONSTELLATION_256QAM_R = + android.hardware.tv.tuner.V1_1.Constants.FrontendDvbtConstellation + .CONSTELLATION_256QAM_R; /** @hide */ @IntDef(flag = true, @@ -366,8 +404,7 @@ public class DvbtFrontendSettings extends FrontendSettings { */ public static final int PLP_MODE_MANUAL = Constants.FrontendDvbtPlpMode.MANUAL; - - private final int mTransmissionMode; + private int mTransmissionMode; private final int mBandwidth; private final int mConstellation; private final int mHierarchy; @@ -489,6 +526,19 @@ public class DvbtFrontendSettings extends FrontendSettings { return mPlpGroupId; } + private static boolean isExtendedTransmissionMode(@TransmissionMode int transmissionMode) { + return transmissionMode == TRANSMISSION_MODE_EXTENDED_8K + || transmissionMode == TRANSMISSION_MODE_EXTENDED_16K + || transmissionMode == TRANSMISSION_MODE_EXTENDED_32K; + } + + private static boolean isExtendedConstellation(@Constellation int constellation) { + return constellation == CONSTELLATION_QPSK_R + || constellation == CONSTELLATION_16QAM_R + || constellation == CONSTELLATION_64QAM_R + || constellation == CONSTELLATION_256QAM_R; + } + /** * Creates a builder for {@link DvbtFrontendSettings}. */ @@ -534,13 +584,23 @@ public class DvbtFrontendSettings extends FrontendSettings { /** * Sets Transmission Mode. * + * <p>{@link #TRANSMISSION_MODE_EXTENDED_8K}, {@link #TRANSMISSION_MODE_EXTENDED_16K} and + * {@link #TRANSMISSION_MODE_EXTENDED_32K} are only supported by Tuner HAL 1.1 or higher. + * Unsupported version would cause no-op. Use {@link TunerVersionChecker.getTunerVersion()} + * to check the version. + * * <p>Default value is {@link #TRANSMISSION_MODE_UNDEFINED}. */ @NonNull public Builder setTransmissionMode(@TransmissionMode int transmissionMode) { - mTransmissionMode = transmissionMode; + if (!isExtendedTransmissionMode(transmissionMode) + || TunerVersionChecker.checkHigherOrEqualVersionTo( + TunerVersionChecker.TUNER_VERSION_1_1, "set TransmissionMode Ext")) { + mTransmissionMode = transmissionMode; + } return this; } + /** * Sets Bandwidth. * @@ -554,11 +614,20 @@ public class DvbtFrontendSettings extends FrontendSettings { /** * Sets Constellation. * + * <p>{@link #CONSTELLATION_QPSK_R}, {@link #CONSTELLATION_16QAM_R}, + * {@link #CONSTELLATION_64QAM_R} and {@link #CONSTELLATION_256QAM_Rare} are only supported + * by Tuner HAL 1.1 or higher. Unsupported version would cause no-op. Use + * {@link TunerVersionChecker.getTunerVersion()} to check the version. + * * <p>Default value is {@link #CONSTELLATION_UNDEFINED}. */ @NonNull public Builder setConstellation(@Constellation int constellation) { - mConstellation = constellation; + if (!isExtendedConstellation(constellation) + || TunerVersionChecker.checkHigherOrEqualVersionTo( + TunerVersionChecker.TUNER_VERSION_1_1, "set Constellation Ext")) { + mConstellation = constellation; + } return this; } /** diff --git a/media/java/android/media/tv/tuner/frontend/FrontendSettings.java b/media/java/android/media/tv/tuner/frontend/FrontendSettings.java index 2f2fa97f4e7a..2147622d349d 100644 --- a/media/java/android/media/tv/tuner/frontend/FrontendSettings.java +++ b/media/java/android/media/tv/tuner/frontend/FrontendSettings.java @@ -17,9 +17,12 @@ package android.media.tv.tuner.frontend; import android.annotation.IntDef; +import android.annotation.IntRange; import android.annotation.LongDef; import android.annotation.SystemApi; import android.hardware.tv.tuner.V1_0.Constants; +import android.media.tv.tuner.Tuner; +import android.media.tv.tuner.TunerVersionChecker; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -34,7 +37,7 @@ public abstract class FrontendSettings { /** @hide */ @IntDef(prefix = "TYPE_", value = {TYPE_UNDEFINED, TYPE_ANALOG, TYPE_ATSC, TYPE_ATSC3, TYPE_DVBC, TYPE_DVBS, - TYPE_DVBT, TYPE_ISDBS, TYPE_ISDBS3, TYPE_ISDBT}) + TYPE_DVBT, TYPE_ISDBS, TYPE_ISDBS3, TYPE_ISDBT, TYPE_DTMB}) @Retention(RetentionPolicy.SOURCE) public @interface Type {} @@ -78,7 +81,10 @@ public abstract class FrontendSettings { * Integrated Services Digital Broadcasting-Terrestrial (ISDB-T) frontend type. */ public static final int TYPE_ISDBT = Constants.FrontendType.ISDBT; - + /** + * Digital Terrestrial Multimedia Broadcast standard (DTMB) frontend type. + */ + public static final int TYPE_DTMB = android.hardware.tv.tuner.V1_1.Constants.FrontendType.DTMB; /** @hide */ @@ -241,9 +247,36 @@ public abstract class FrontendSettings { */ public static final long FEC_77_90 = Constants.FrontendInnerFec.FEC_77_90; + /** @hide */ + @IntDef(prefix = "FRONTEND_SPECTRAL_INVERSION_", + value = {FRONTEND_SPECTRAL_INVERSION_UNDEFINED, FRONTEND_SPECTRAL_INVERSION_NORMAL, + FRONTEND_SPECTRAL_INVERSION_INVERTED}) + @Retention(RetentionPolicy.SOURCE) + public @interface FrontendSpectralInversion {} + + /** + * Spectral Inversion Type undefined. + */ + public static final int FRONTEND_SPECTRAL_INVERSION_UNDEFINED = + Constants.FrontendDvbcSpectralInversion.UNDEFINED; + /** + * Normal Spectral Inversion. + */ + public static final int FRONTEND_SPECTRAL_INVERSION_NORMAL = + Constants.FrontendDvbcSpectralInversion.NORMAL; + /** + * Inverted Spectral Inversion. + */ + public static final int FRONTEND_SPECTRAL_INVERSION_INVERTED = + Constants.FrontendDvbcSpectralInversion.INVERTED; + private final int mFrequency; + // End frequency is only supported in Tuner 1.1 or higher. + private int mEndFrequency = Tuner.INVALID_FRONTEND_SETTING_FREQUENCY; + // General spectral inversion is only supported in Tuner 1.1 or higher. + private int mSpectralInversion = FRONTEND_SPECTRAL_INVERSION_UNDEFINED; FrontendSettings(int frequency) { mFrequency = frequency; @@ -263,4 +296,57 @@ public abstract class FrontendSettings { public int getFrequency() { return mFrequency; } + + /** + * Get the end frequency. + * + * @return the end frequency in Hz. + */ + public int getEndFrequency() { + return mEndFrequency; + } + + /** + * Get the spectral inversion. + * + * @return the value of the spectral inversion. + */ + @FrontendSpectralInversion + public int getFrontendSpectralInversion() { + return mSpectralInversion; + } + + /** + * Set Spectral Inversion. + * + * <p>This API is only supported by Tuner HAL 1.1 or higher. Unsupported version would cause + * no-op. Use {@link TunerVersionChecker.getTunerVersion()} to check the version. + * + * @param inversion the value to set as the spectral inversion. Default value is {@link + * #FRONTEND_SPECTRAL_INVERSION_UNDEFINED}. + */ + public void setSpectralInversion(@FrontendSpectralInversion int inversion) { + if (TunerVersionChecker.checkHigherOrEqualVersionTo( + TunerVersionChecker.TUNER_VERSION_1_1, "setSpectralInversion")) { + mSpectralInversion = inversion; + } + } + + /** + * Set End Frequency. This API is only supported with Tuner HAL 1.1 or higher. Otherwise it + * would be no-op. + * + * <p>This API is only supported by Tuner HAL 1.1 or higher. Unsupported version would cause + * no-op. Use {@link TunerVersionChecker.getTunerVersion()} to check the version. + * + * @param endFrequency the end frequency used during blind scan. The default value is + * {@link android.media.tv.tuner.Tuner#INVALID_FRONTEND_SETTING_FREQUENCY}. + */ + @IntRange(from = 1) + public void setEndFrequency(int endFrequency) { + if (TunerVersionChecker.checkHigherOrEqualVersionTo( + TunerVersionChecker.TUNER_VERSION_1_1, "setEndFrequency")) { + mEndFrequency = endFrequency; + } + } } diff --git a/media/java/android/media/tv/tuner/frontend/FrontendStatus.java b/media/java/android/media/tv/tuner/frontend/FrontendStatus.java index c265bb9e6050..dd9347cd6e5d 100644 --- a/media/java/android/media/tv/tuner/frontend/FrontendStatus.java +++ b/media/java/android/media/tv/tuner/frontend/FrontendStatus.java @@ -21,6 +21,7 @@ import android.annotation.NonNull; import android.annotation.SystemApi; import android.hardware.tv.tuner.V1_0.Constants; import android.media.tv.tuner.Lnb; +import android.media.tv.tuner.TunerVersionChecker; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -43,7 +44,13 @@ public class FrontendStatus { FRONTEND_STATUS_TYPE_EWBS, FRONTEND_STATUS_TYPE_AGC, FRONTEND_STATUS_TYPE_LNA, FRONTEND_STATUS_TYPE_LAYER_ERROR, FRONTEND_STATUS_TYPE_MER, FRONTEND_STATUS_TYPE_FREQ_OFFSET, FRONTEND_STATUS_TYPE_HIERARCHY, - FRONTEND_STATUS_TYPE_RF_LOCK, FRONTEND_STATUS_TYPE_ATSC3_PLP_INFO}) + FRONTEND_STATUS_TYPE_RF_LOCK, FRONTEND_STATUS_TYPE_ATSC3_PLP_INFO, + FRONTEND_STATUS_TYPE_BERS, FRONTEND_STATUS_TYPE_CODERATES, + FRONTEND_STATUS_TYPE_BANDWIDTH, FRONTEND_STATUS_TYPE_GUARD_INTERVAL, + FRONTEND_STATUS_TYPE_TRANSMISSION_MODE, FRONTEND_STATUS_TYPE_UEC, + FRONTEND_STATUS_TYPE_T2_SYSTEM_ID, FRONTEND_STATUS_TYPE_INTERLEAVINGS, + FRONTEND_STATUS_TYPE_ISDBT_SEGMENTS, FRONTEND_STATUS_TYPE_TS_DATA_RATES, + FRONTEND_STATUS_TYPE_MODULATIONS_EXT}) @Retention(RetentionPolicy.SOURCE) public @interface FrontendStatusType {} @@ -145,10 +152,83 @@ public class FrontendStatus { */ public static final int FRONTEND_STATUS_TYPE_ATSC3_PLP_INFO = Constants.FrontendStatusType.ATSC3_PLP_INFO; - + /** + * BERS Type. Only supported in Tuner HAL 1.1 or higher. + */ + public static final int FRONTEND_STATUS_TYPE_BERS = + android.hardware.tv.tuner.V1_1.Constants.FrontendStatusTypeExt1_1.BERS; + /** + * Coderate Type. Only supported in Tuner HAL 1.1 or higher. + */ + public static final int FRONTEND_STATUS_TYPE_CODERATES = + android.hardware.tv.tuner.V1_1.Constants.FrontendStatusTypeExt1_1.CODERATES; + /** + * Bandwidth Type. Only supported in Tuner HAL 1.1 or higher. + */ + public static final int FRONTEND_STATUS_TYPE_BANDWIDTH = + android.hardware.tv.tuner.V1_1.Constants.FrontendStatusTypeExt1_1.BANDWIDTH; + /** + * Guard Interval Type. Only supported in Tuner HAL 1.1 or higher. + */ + public static final int FRONTEND_STATUS_TYPE_GUARD_INTERVAL = + android.hardware.tv.tuner.V1_1.Constants.FrontendStatusTypeExt1_1.GUARD_INTERVAL; + /** + * Transmission Mode Type. Only supported in Tuner HAL 1.1 or higher. + */ + public static final int FRONTEND_STATUS_TYPE_TRANSMISSION_MODE = + android.hardware.tv.tuner.V1_1.Constants.FrontendStatusTypeExt1_1.TRANSMISSION_MODE; + /** + * UEC Type. Only supported in Tuner HAL 1.1 or higher. + */ + public static final int FRONTEND_STATUS_TYPE_UEC = + android.hardware.tv.tuner.V1_1.Constants.FrontendStatusTypeExt1_1.UEC; + /** + * T2 System Id Type. Only supported in Tuner HAL 1.1 or higher. + */ + public static final int FRONTEND_STATUS_TYPE_T2_SYSTEM_ID = + android.hardware.tv.tuner.V1_1.Constants.FrontendStatusTypeExt1_1.T2_SYSTEM_ID; + /** + * Interleavings Type. Only supported in Tuner HAL 1.1 or higher. + */ + public static final int FRONTEND_STATUS_TYPE_INTERLEAVINGS = + android.hardware.tv.tuner.V1_1.Constants.FrontendStatusTypeExt1_1.INTERLEAVINGS; + /** + * ISDBT Segments Type. Only supported in Tuner HAL 1.1 or higher. + */ + public static final int FRONTEND_STATUS_TYPE_ISDBT_SEGMENTS = + android.hardware.tv.tuner.V1_1.Constants.FrontendStatusTypeExt1_1.ISDBT_SEGMENTS; + /** + * TS Data Rates Type. Only supported in Tuner HAL 1.1 or higher. + */ + public static final int FRONTEND_STATUS_TYPE_TS_DATA_RATES = + android.hardware.tv.tuner.V1_1.Constants.FrontendStatusTypeExt1_1.TS_DATA_RATES; + /** + * Extended Modulations Type. Only supported in Tuner HAL 1.1 or higher. + */ + public static final int FRONTEND_STATUS_TYPE_MODULATIONS_EXT = + android.hardware.tv.tuner.V1_1.Constants.FrontendStatusTypeExt1_1.MODULATIONS; /** @hide */ @IntDef(value = { + AtscFrontendSettings.MODULATION_UNDEFINED, + AtscFrontendSettings.MODULATION_AUTO, + AtscFrontendSettings.MODULATION_MOD_8VSB, + AtscFrontendSettings.MODULATION_MOD_16VSB, + Atsc3FrontendSettings.MODULATION_UNDEFINED, + Atsc3FrontendSettings.MODULATION_AUTO, + Atsc3FrontendSettings.MODULATION_MOD_QPSK, + Atsc3FrontendSettings.MODULATION_MOD_16QAM, + Atsc3FrontendSettings.MODULATION_MOD_64QAM, + Atsc3FrontendSettings.MODULATION_MOD_256QAM, + Atsc3FrontendSettings.MODULATION_MOD_1024QAM, + Atsc3FrontendSettings.MODULATION_MOD_4096QAM, + DtmbFrontendSettings.MODULATION_CONSTELLATION_UNDEFINED, + DtmbFrontendSettings.MODULATION_CONSTELLATION_AUTO, + DtmbFrontendSettings.MODULATION_CONSTELLATION_4QAM, + DtmbFrontendSettings.MODULATION_CONSTELLATION_4QAM_NR, + DtmbFrontendSettings.MODULATION_CONSTELLATION_16QAM, + DtmbFrontendSettings.MODULATION_CONSTELLATION_32QAM, + DtmbFrontendSettings.MODULATION_CONSTELLATION_64QAM, DvbcFrontendSettings.MODULATION_UNDEFINED, DvbcFrontendSettings.MODULATION_AUTO, DvbcFrontendSettings.MODULATION_MOD_16QAM, @@ -171,6 +251,16 @@ public class FrontendStatus { DvbsFrontendSettings.MODULATION_MOD_128APSK, DvbsFrontendSettings.MODULATION_MOD_256APSK, DvbsFrontendSettings.MODULATION_MOD_RESERVED, + DvbtFrontendSettings.CONSTELLATION_UNDEFINED, + DvbtFrontendSettings.CONSTELLATION_AUTO, + DvbtFrontendSettings.CONSTELLATION_QPSK, + DvbtFrontendSettings.CONSTELLATION_16QAM, + DvbtFrontendSettings.CONSTELLATION_64QAM, + DvbtFrontendSettings.CONSTELLATION_256QAM, + DvbtFrontendSettings.CONSTELLATION_QPSK_R, + DvbtFrontendSettings.CONSTELLATION_16QAM_R, + DvbtFrontendSettings.CONSTELLATION_64QAM_R, + DvbtFrontendSettings.CONSTELLATION_256QAM_R, IsdbsFrontendSettings.MODULATION_UNDEFINED, IsdbsFrontendSettings.MODULATION_AUTO, IsdbsFrontendSettings.MODULATION_MOD_BPSK, @@ -192,6 +282,101 @@ public class FrontendStatus { @Retention(RetentionPolicy.SOURCE) public @interface FrontendModulation {} + /** @hide */ + @IntDef(value = { + Atsc3FrontendSettings.TIME_INTERLEAVE_MODE_UNDEFINED, + Atsc3FrontendSettings.TIME_INTERLEAVE_MODE_AUTO, + Atsc3FrontendSettings.TIME_INTERLEAVE_MODE_CTI, + Atsc3FrontendSettings.TIME_INTERLEAVE_MODE_HTI, + DtmbFrontendSettings.TIME_INTERLEAVE_MODE_UNDEFINED, + DtmbFrontendSettings.TIME_INTERLEAVE_MODE_AUTO, + DtmbFrontendSettings.TIME_INTERLEAVE_MODE_TIMER_INT_240, + DtmbFrontendSettings.TIME_INTERLEAVE_MODE_TIMER_INT_720, + DvbcFrontendSettings.TIME_INTERLEAVE_MODE_UNDEFINED, + DvbcFrontendSettings.TIME_INTERLEAVE_MODE_AUTO, + DvbcFrontendSettings.TIME_INTERLEAVE_MODE_128_1_0, + DvbcFrontendSettings.TIME_INTERLEAVE_MODE_128_1_1, + DvbcFrontendSettings.TIME_INTERLEAVE_MODE_64_2, + DvbcFrontendSettings.TIME_INTERLEAVE_MODE_32_4, + DvbcFrontendSettings.TIME_INTERLEAVE_MODE_16_8, + DvbcFrontendSettings.TIME_INTERLEAVE_MODE_8_16, + DvbcFrontendSettings.TIME_INTERLEAVE_MODE_128_2, + DvbcFrontendSettings.TIME_INTERLEAVE_MODE_128_3, + DvbcFrontendSettings.TIME_INTERLEAVE_MODE_128_4}) + @Retention(RetentionPolicy.SOURCE) + public @interface FrontendInterleaveMode {} + + /** @hide */ + @IntDef(value = { + Atsc3FrontendSettings.BANDWIDTH_UNDEFINED, + Atsc3FrontendSettings.BANDWIDTH_AUTO, + Atsc3FrontendSettings.BANDWIDTH_BANDWIDTH_6MHZ, + Atsc3FrontendSettings.BANDWIDTH_BANDWIDTH_7MHZ, + Atsc3FrontendSettings.BANDWIDTH_BANDWIDTH_8MHZ, + DtmbFrontendSettings.BANDWIDTH_UNDEFINED, + DtmbFrontendSettings.BANDWIDTH_AUTO, + DtmbFrontendSettings.BANDWIDTH_6MHZ, + DtmbFrontendSettings.BANDWIDTH_8MHZ, + DvbtFrontendSettings.BANDWIDTH_UNDEFINED, + DvbtFrontendSettings.BANDWIDTH_AUTO, + DvbtFrontendSettings.BANDWIDTH_8MHZ, + DvbtFrontendSettings.BANDWIDTH_7MHZ, + DvbtFrontendSettings.BANDWIDTH_6MHZ, + DvbtFrontendSettings.BANDWIDTH_5MHZ, + DvbtFrontendSettings.BANDWIDTH_1_7MHZ, + DvbtFrontendSettings.BANDWIDTH_10MHZ, + IsdbtFrontendSettings.BANDWIDTH_UNDEFINED, + IsdbtFrontendSettings.BANDWIDTH_AUTO, + IsdbtFrontendSettings.BANDWIDTH_8MHZ, + IsdbtFrontendSettings.BANDWIDTH_7MHZ, + IsdbtFrontendSettings.BANDWIDTH_6MHZ}) + @Retention(RetentionPolicy.SOURCE) + public @interface FrontendBandwidth {} + + /** @hide */ + @IntDef(value = { + DtmbFrontendSettings.TRANSMISSION_MODE_UNDEFINED, + DtmbFrontendSettings.TRANSMISSION_MODE_AUTO, + DtmbFrontendSettings.TRANSMISSION_MODE_C1, + DtmbFrontendSettings.TRANSMISSION_MODE_C3780, + DvbtFrontendSettings.TRANSMISSION_MODE_UNDEFINED, + DvbtFrontendSettings.TRANSMISSION_MODE_AUTO, + DvbtFrontendSettings.TRANSMISSION_MODE_2K, + DvbtFrontendSettings.TRANSMISSION_MODE_8K, + DvbtFrontendSettings.TRANSMISSION_MODE_4K, + DvbtFrontendSettings.TRANSMISSION_MODE_1K, + DvbtFrontendSettings.TRANSMISSION_MODE_16K, + DvbtFrontendSettings.TRANSMISSION_MODE_32K, + IsdbtFrontendSettings.MODE_UNDEFINED, + IsdbtFrontendSettings.MODE_AUTO, + IsdbtFrontendSettings.MODE_1, + IsdbtFrontendSettings.MODE_2, + IsdbtFrontendSettings.MODE_3}) + @Retention(RetentionPolicy.SOURCE) + public @interface FrontendTransmissionMode {} + + /** @hide */ + @IntDef(value = { + DtmbFrontendSettings.GUARD_INTERVAL_UNDEFINED, + DtmbFrontendSettings.GUARD_INTERVAL_AUTO, + DtmbFrontendSettings.GUARD_INTERVAL_PN_420_VARIOUS, + DtmbFrontendSettings.GUARD_INTERVAL_PN_595_CONST, + DtmbFrontendSettings.GUARD_INTERVAL_PN_945_VARIOUS, + DtmbFrontendSettings.GUARD_INTERVAL_PN_420_CONST, + DtmbFrontendSettings.GUARD_INTERVAL_PN_945_CONST, + DtmbFrontendSettings.GUARD_INTERVAL_PN_RESERVED, + DvbtFrontendSettings.GUARD_INTERVAL_UNDEFINED, + DvbtFrontendSettings.GUARD_INTERVAL_AUTO, + DvbtFrontendSettings.GUARD_INTERVAL_1_32, + DvbtFrontendSettings.GUARD_INTERVAL_1_16, + DvbtFrontendSettings.GUARD_INTERVAL_1_8, + DvbtFrontendSettings.GUARD_INTERVAL_1_4, + DvbtFrontendSettings.GUARD_INTERVAL_1_128, + DvbtFrontendSettings.GUARD_INTERVAL_19_128, + DvbtFrontendSettings.GUARD_INTERVAL_19_256, + DvbtFrontendSettings.GUARD_INTERVAL_19_128}) + @Retention(RetentionPolicy.SOURCE) + public @interface FrontendGuardInterval {} private Boolean mIsDemodLocked; private Integer mSnr; @@ -215,6 +400,18 @@ public class FrontendStatus { private Integer mHierarchy; private Boolean mIsRfLocked; private Atsc3PlpTuningInfo[] mPlpInfo; + private int[] mBers; + private int[] mCodeRates; + private Integer mBandwidth; + private Integer mGuardInterval; + private Integer mTransmissionMode; + private Integer mUec; + private Integer mSystemId; + private int[] mInterleaving; + private int[] mTsDataRate; + private int[] mIsdbtSegment; + private int[] mModulationsExt; + // Constructed and fields set by JNI code. private FrontendStatus() { @@ -323,7 +520,7 @@ public class FrontendStatus { /** * Gets Spectral Inversion for DVBC. */ - @DvbcFrontendSettings.SpectralInversion + @FrontendSettings.FrontendSpectralInversion public int getSpectralInversion() { if (mInversion == null) { throw new IllegalStateException(); @@ -437,6 +634,182 @@ public class FrontendStatus { } /** + * Gets an array of BERS status. + * + * <p>This status query is only supported by Tuner HAL 1.1 or higher. Use + * {@link TunerVersionChecker.getTunerVersion()} to check the version. + */ + @NonNull + public int[] getBers() { + TunerVersionChecker.checkHigherOrEqualVersionTo( + TunerVersionChecker.TUNER_VERSION_1_1, "getBers status"); + if (mBers == null) { + throw new IllegalStateException(); + } + return mBers; + } + + /** + * Gets an array of code rates status. + * + * <p>This status query is only supported by Tuner HAL 1.1 or higher. Use + * {@link TunerVersionChecker.getTunerVersion()} to check the version. + */ + @NonNull + public int[] getCodeRates() { + TunerVersionChecker.checkHigherOrEqualVersionTo( + TunerVersionChecker.TUNER_VERSION_1_1, "getCodeRates status"); + if (mCodeRates == null) { + throw new IllegalStateException(); + } + return mCodeRates; + } + + /** + * Gets bandwidth status. + * + * <p>This status query is only supported by Tuner HAL 1.1 or higher. Use + * {@link TunerVersionChecker.getTunerVersion()} to check the version. + */ + @FrontendBandwidth + public int getBandwidth() { + TunerVersionChecker.checkHigherOrEqualVersionTo( + TunerVersionChecker.TUNER_VERSION_1_1, "getBandwidth status"); + if (mBandwidth == null) { + throw new IllegalStateException(); + } + return mBandwidth; + } + + /** + * Gets guard interval status. + * + * <p>This status query is only supported by Tuner HAL 1.1 or higher. Use + * {@link TunerVersionChecker.getTunerVersion()} to check the version. + */ + @FrontendGuardInterval + public int getGuardInterval() { + TunerVersionChecker.checkHigherOrEqualVersionTo( + TunerVersionChecker.TUNER_VERSION_1_1, "getGuardInterval status"); + if (mGuardInterval == null) { + throw new IllegalStateException(); + } + return mGuardInterval; + } + + /** + * Gets tansmission mode status. + * + * <p>This status query is only supported by Tuner HAL 1.1 or higher. Use + * {@link TunerVersionChecker.getTunerVersion()} to check the version. + */ + @FrontendTransmissionMode + public int getTransmissionMode() { + TunerVersionChecker.checkHigherOrEqualVersionTo( + TunerVersionChecker.TUNER_VERSION_1_1, "getTransmissionMode status"); + if (mTransmissionMode == null) { + throw new IllegalStateException(); + } + return mTransmissionMode; + } + + /** + * Gets UEC status. + * + * <p>This status query is only supported by Tuner HAL 1.1 or higher. Use + * {@link TunerVersionChecker.getTunerVersion()} to check the version. + */ + public int getUec() { + TunerVersionChecker.checkHigherOrEqualVersionTo( + TunerVersionChecker.TUNER_VERSION_1_1, "getUec status"); + if (mUec == null) { + throw new IllegalStateException(); + } + return mUec; + } + + /** + * Gets system id status. + * + * <p>This status query is only supported by Tuner HAL 1.1 or higher. Use + * {@link TunerVersionChecker.getTunerVersion()} to check the version. + */ + public int getSystemId() { + TunerVersionChecker.checkHigherOrEqualVersionTo( + TunerVersionChecker.TUNER_VERSION_1_1, "getSystemId status"); + if (mSystemId == null) { + throw new IllegalStateException(); + } + return mSystemId; + } + + /** + * Gets an array of interleaving status. Array value should be withink {@link + * FrontendInterleaveMode}. + * + * <p>This status query is only supported by Tuner HAL 1.1 or higher. Use + * {@link TunerVersionChecker.getTunerVersion()} to check the version. + */ + @NonNull + public int[] getInterleaving() { + TunerVersionChecker.checkHigherOrEqualVersionTo( + TunerVersionChecker.TUNER_VERSION_1_1, "getInterleaving status"); + if (mInterleaving == null) { + throw new IllegalStateException(); + } + return mInterleaving; + } + + /** + * Gets an array of isdbt segment status. + * + * <p>This status query is only supported by Tuner HAL 1.1 or higher. Use + * {@link TunerVersionChecker.getTunerVersion()} to check the version. + */ + @NonNull + public int[] getIsdbtSegment() { + TunerVersionChecker.checkHigherOrEqualVersionTo( + TunerVersionChecker.TUNER_VERSION_1_1, "getIsdbtSegment status"); + if (mIsdbtSegment == null) { + throw new IllegalStateException(); + } + return mIsdbtSegment; + } + + /** + * Gets an array of TS data rate status. + * + * <p>This status query is only supported by Tuner HAL 1.1 or higher. Use + * {@link TunerVersionChecker.getTunerVersion()} to check the version. + */ + @NonNull + public int[] getTsDataRate() { + TunerVersionChecker.checkHigherOrEqualVersionTo( + TunerVersionChecker.TUNER_VERSION_1_1, "getTsDataRate status"); + if (mTsDataRate == null) { + throw new IllegalStateException(); + } + return mTsDataRate; + } + + /** + * Gets an array of the extended modulations status. Array value should be withink {@link + * FrontendModulation}. + * + * <p>This status query is only supported by Tuner HAL 1.1 or higher. Use + * {@link TunerVersionChecker.getTunerVersion()} to check the version. + */ + @NonNull + public int[] getModulationsExt() { + TunerVersionChecker.checkHigherOrEqualVersionTo( + TunerVersionChecker.TUNER_VERSION_1_1, "getModulationsExt status"); + if (mModulationsExt == null) { + throw new IllegalStateException(); + } + return mModulationsExt; + } + + /** * Status for each tuning Physical Layer Pipes. */ public static class Atsc3PlpTuningInfo { diff --git a/media/java/android/media/tv/tuner/frontend/ScanCallback.java b/media/java/android/media/tv/tuner/frontend/ScanCallback.java index b0491fdbde1a..9bf7a5dd40cb 100644 --- a/media/java/android/media/tv/tuner/frontend/ScanCallback.java +++ b/media/java/android/media/tv/tuner/frontend/ScanCallback.java @@ -70,4 +70,9 @@ public interface ScanCallback { /** Frontend signal type. */ void onSignalTypeReported(@AnalogFrontendSettings.SignalType int signalType); + /** Frontend modulation reported. */ + default void onModulationReported(@FrontendStatus.FrontendModulation int modulation) {} + + /** Frontend scan message priority reported. */ + default void onPriorityReported(boolean isHighPriority) {} } diff --git a/media/jni/android_media_MediaMetadataRetriever.cpp b/media/jni/android_media_MediaMetadataRetriever.cpp index 6fbd29cb8623..126897a908f8 100644 --- a/media/jni/android_media_MediaMetadataRetriever.cpp +++ b/media/jni/android_media_MediaMetadataRetriever.cpp @@ -704,7 +704,7 @@ static const JNINativeMethod nativeMethods[] = { (void *)android_media_MediaMetadataRetriever_setDataSourceAndHeaders }, - {"setDataSource", "(Ljava/io/FileDescriptor;JJ)V", + {"_setDataSource", "(Ljava/io/FileDescriptor;JJ)V", (void *)android_media_MediaMetadataRetriever_setDataSourceFD}, {"_setDataSource", "(Landroid/media/MediaDataSource;)V", (void *)android_media_MediaMetadataRetriever_setDataSourceCallback}, diff --git a/media/jni/android_media_Utils.cpp b/media/jni/android_media_Utils.cpp index 2ef7b9e22b84..b6c47fca52f9 100644 --- a/media/jni/android_media_Utils.cpp +++ b/media/jni/android_media_Utils.cpp @@ -140,6 +140,27 @@ status_t getLockedImageInfo(LockedImage* buffer, int idx, fmt = applyFormatOverrides(fmt, containerFormat); switch (fmt) { case HAL_PIXEL_FORMAT_YCbCr_420_888: + // Width and height should be multiple of 2. Wrong dataSize would be returned otherwise. + if (buffer->width % 2 != 0) { + ALOGE("YCbCr_420_888: width (%d) should be a multiple of 2", buffer->width); + return BAD_VALUE; + } + + if (buffer->height % 2 != 0) { + ALOGE("YCbCr_420_888: height (%d) should be a multiple of 2", buffer->height); + return BAD_VALUE; + } + + if (buffer->width <= 0) { + ALOGE("YCbCr_420_888: width (%d) should be a > 0", buffer->width); + return BAD_VALUE; + } + + if (buffer->height <= 0) { + ALOGE("YCbCr_420_888: height (%d) should be a > 0", buffer->height); + return BAD_VALUE; + } + pData = (idx == 0) ? buffer->data : @@ -160,6 +181,27 @@ status_t getLockedImageInfo(LockedImage* buffer, int idx, break; // NV21 case HAL_PIXEL_FORMAT_YCrCb_420_SP: + // Width and height should be multiple of 2. Wrong dataSize would be returned otherwise. + if (buffer->width % 2 != 0) { + ALOGE("YCrCb_420_SP: width (%d) should be a multiple of 2", buffer->width); + return BAD_VALUE; + } + + if (buffer->height % 2 != 0) { + ALOGE("YCrCb_420_SP: height (%d) should be a multiple of 2", buffer->height); + return BAD_VALUE; + } + + if (buffer->width <= 0) { + ALOGE("YCrCb_420_SP: width (%d) should be a > 0", buffer->width); + return BAD_VALUE; + } + + if (buffer->height <= 0) { + ALOGE("YCrCb_420_SP: height (%d) should be a > 0", buffer->height); + return BAD_VALUE; + } + cr = buffer->data + (buffer->stride * buffer->height); cb = cr + 1; // only map until last pixel @@ -178,6 +220,27 @@ status_t getLockedImageInfo(LockedImage* buffer, int idx, rStride = buffer->width; break; case HAL_PIXEL_FORMAT_YV12: + // Width and height should be multiple of 2. Wrong dataSize would be returned otherwise. + if (buffer->width % 2 != 0) { + ALOGE("YV12: width (%d) should be a multiple of 2", buffer->width); + return BAD_VALUE; + } + + if (buffer->height % 2 != 0) { + ALOGE("YV12: height (%d) should be a multiple of 2", buffer->height); + return BAD_VALUE; + } + + if (buffer->width <= 0) { + ALOGE("YV12: width (%d) should be a > 0", buffer->width); + return BAD_VALUE; + } + + if (buffer->height <= 0) { + ALOGE("YV12: height (%d) should be a > 0", buffer->height); + return BAD_VALUE; + } + // Y and C stride need to be 16 pixel aligned. LOG_ALWAYS_FATAL_IF(buffer->stride % 16, "Stride is not 16 pixel aligned %d", buffer->stride); @@ -344,6 +407,11 @@ status_t lockImageFromBuffer(sp<GraphicBuffer> buffer, uint32_t inUsage, int flexFormat = format; if (isPossiblyYUV(format)) { res = buffer->lockAsyncYCbCr(inUsage, rect, &ycbcr, fenceFd); + + if (res != OK) { + ALOGW("lockAsyncYCbCr failed with error %d", res); + } + pData = ycbcr.y; flexFormat = HAL_PIXEL_FORMAT_YCbCr_420_888; } diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp index e0afe29de7e6..1be0d4439e69 100644 --- a/media/jni/android_media_tv_Tuner.cpp +++ b/media/jni/android_media_tv_Tuner.cpp @@ -132,8 +132,34 @@ using ::android::hardware::tv::tuner::V1_0::LnbTone; using ::android::hardware::tv::tuner::V1_0::LnbVoltage; using ::android::hardware::tv::tuner::V1_0::PlaybackSettings; using ::android::hardware::tv::tuner::V1_0::RecordSettings; +using ::android::hardware::tv::tuner::V1_1::AudioStreamType; +using ::android::hardware::tv::tuner::V1_1::AvStreamType; using ::android::hardware::tv::tuner::V1_1::Constant; using ::android::hardware::tv::tuner::V1_1::Constant64Bit; +using ::android::hardware::tv::tuner::V1_1::FrontendAnalogAftFlag; +using ::android::hardware::tv::tuner::V1_1::FrontendAnalogSettingsExt1_1; +using ::android::hardware::tv::tuner::V1_1::FrontendBandwidth; +using ::android::hardware::tv::tuner::V1_1::FrontendCableTimeInterleaveMode; +using ::android::hardware::tv::tuner::V1_1::FrontendDvbsScanType; +using ::android::hardware::tv::tuner::V1_1::FrontendDvbcSettingsExt1_1; +using ::android::hardware::tv::tuner::V1_1::FrontendDvbsSettingsExt1_1; +using ::android::hardware::tv::tuner::V1_1::FrontendDvbtSettingsExt1_1; +using ::android::hardware::tv::tuner::V1_1::FrontendDtmbBandwidth; +using ::android::hardware::tv::tuner::V1_1::FrontendDtmbCapabilities; +using ::android::hardware::tv::tuner::V1_1::FrontendDtmbCodeRate; +using ::android::hardware::tv::tuner::V1_1::FrontendDtmbGuardInterval; +using ::android::hardware::tv::tuner::V1_1::FrontendDtmbModulation; +using ::android::hardware::tv::tuner::V1_1::FrontendDtmbSettings; +using ::android::hardware::tv::tuner::V1_1::FrontendDtmbTimeInterleaveMode; +using ::android::hardware::tv::tuner::V1_1::FrontendDtmbTransmissionMode; +using ::android::hardware::tv::tuner::V1_1::FrontendGuardInterval; +using ::android::hardware::tv::tuner::V1_1::FrontendInterleaveMode; +using ::android::hardware::tv::tuner::V1_1::FrontendModulation; +using ::android::hardware::tv::tuner::V1_1::FrontendSpectralInversion; +using ::android::hardware::tv::tuner::V1_1::FrontendStatusExt1_1; +using ::android::hardware::tv::tuner::V1_1::FrontendStatusTypeExt1_1; +using ::android::hardware::tv::tuner::V1_1::FrontendTransmissionMode; +using ::android::hardware::tv::tuner::V1_1::VideoStreamType; struct fields_t { jfieldID tunerContext; @@ -309,9 +335,9 @@ C2DataIdInfo::C2DataIdInfo(uint32_t index, uint64_t value) : C2Param(kParamSize, /////////////// MediaEvent /////////////////////// -MediaEvent::MediaEvent(sp<IFilter> iFilter, hidl_handle avHandle, - uint64_t dataId, uint64_t dataLength, jobject obj) : mIFilter(iFilter), - mDataId(dataId), mDataLength(dataLength), mBuffer(nullptr), +MediaEvent::MediaEvent(sp<Filter> filter, hidl_handle avHandle, + uint64_t dataId, uint64_t dataSize, jobject obj) : mFilter(filter), + mDataId(dataId), mDataSize(dataSize), mBuffer(nullptr), mDataIdRefCnt(0), mAvHandleRefCnt(0), mIonHandle(nullptr) { JNIEnv *env = AndroidRuntime::getJNIEnv(); mMediaEventObj = env->NewWeakGlobalRef(obj); @@ -335,7 +361,8 @@ MediaEvent::~MediaEvent() { void MediaEvent::finalize() { if (mAvHandleRefCnt == 0) { - mIFilter->releaseAvHandle(hidl_handle(mAvHandle), mDataIdRefCnt == 0 ? mDataId : 0); + mFilter->mFilterSp->releaseAvHandle( + hidl_handle(mAvHandle), mDataIdRefCnt == 0 ? mDataId : 0); native_handle_close(mAvHandle); } } @@ -348,7 +375,47 @@ jobject MediaEvent::getLinearBlock() { if (mLinearBlockObj != NULL) { return mLinearBlockObj; } - mIonHandle = new C2HandleIon(dup(mAvHandle->data[0]), mDataLength); + + int fd; + int numInts = 0; + int memIndex; + int dataSize; + if (mAvHandle->numFds == 0) { + if (mFilter->mAvSharedHandle == NULL) { + ALOGE("Shared AV memory handle is not initialized."); + return NULL; + } + if (mFilter->mAvSharedHandle->numFds == 0) { + ALOGE("Shared AV memory handle is empty."); + return NULL; + } + fd = mFilter->mAvSharedHandle->data[0]; + dataSize = mFilter->mAvSharedMemSize; + numInts = mFilter->mAvSharedHandle->numInts; + if (numInts > 0) { + // If the first int in the shared native handle has value, use it as the index + memIndex = mFilter->mAvSharedHandle->data[mFilter->mAvSharedHandle->numFds]; + } + } else { + fd = mAvHandle->data[0]; + dataSize = mDataSize; + numInts = mAvHandle->numInts; + if (numInts > 0) { + // Otherwise if the first int in the av native handle returned from the filter + // event has value, use it as the index + memIndex = mAvHandle->data[mAvHandle->numFds]; + } else { + if (mFilter->mAvSharedHandle != NULL) { + numInts = mFilter->mAvSharedHandle->numInts; + if (numInts > 0) { + // If the first int in the shared native handle has value, use it as the index + memIndex = mFilter->mAvSharedHandle->data[mFilter->mAvSharedHandle->numFds]; + } + } + } + } + + mIonHandle = new C2HandleIon(dup(fd), dataSize); std::shared_ptr<C2LinearBlock> block = _C2BlockFactory::CreateLinearBlock(mIonHandle); if (block != nullptr) { // CreateLinearBlock delete mIonHandle after it create block successfully. @@ -357,13 +424,11 @@ jobject MediaEvent::getLinearBlock() { JNIEnv *env = AndroidRuntime::getJNIEnv(); std::unique_ptr<JMediaCodecLinearBlock> context{new JMediaCodecLinearBlock}; context->mBlock = block; - std::shared_ptr<C2Buffer> pC2Buffer = context->toC2Buffer(0, mDataLength); + std::shared_ptr<C2Buffer> pC2Buffer = context->toC2Buffer(0, dataSize); context->mBuffer = pC2Buffer; mC2Buffer = pC2Buffer; - if (mAvHandle->numInts > 0) { - // use first int in the native_handle as the index - int index = mAvHandle->data[mAvHandle->numFds]; - std::shared_ptr<C2Param> c2param = std::make_shared<C2DataIdInfo>(index, mDataId); + if (numInts > 0) { + std::shared_ptr<C2Param> c2param = std::make_shared<C2DataIdInfo>(memIndex, mDataId); std::shared_ptr<C2Info> info(std::static_pointer_cast<C2Info>(c2param)); pC2Buffer->setInfo(info); } @@ -470,7 +535,7 @@ jobjectArray FilterCallback::getMediaEvent( if (mediaEvent.avMemory.getNativeHandle() != NULL || mediaEvent.avDataId != 0) { sp<MediaEvent> mediaEventSp = - new MediaEvent(mIFilter, mediaEvent.avMemory, + new MediaEvent(mFilter, mediaEvent.avMemory, mediaEvent.avDataId, dataLength + offset, obj); mediaEventSp->mAvHandleRefCnt++; env->SetLongField(obj, eventContext, (jlong) mediaEventSp.get()); @@ -690,7 +755,7 @@ Return<void> FilterCallback::onFilterEvent_1_1(const DemuxFilterEvent& filterEve } } env->CallVoidMethod( - mFilter, + mFilter->mFilterObj, gFields.onFilterEventID, array); return Void(); @@ -709,7 +774,7 @@ Return<void> FilterCallback::onFilterStatus(const DemuxFilterStatus status) { ALOGD("FilterCallback::onFilterStatus"); JNIEnv *env = AndroidRuntime::getJNIEnv(); env->CallVoidMethod( - mFilter, + mFilter->mFilterObj, gFields.onFilterStatusID, (jint)status); return Void(); @@ -717,17 +782,11 @@ Return<void> FilterCallback::onFilterStatus(const DemuxFilterStatus status) { void FilterCallback::setFilter(const sp<Filter> filter) { ALOGD("FilterCallback::setFilter"); - mFilter = filter->mFilterObj; - mIFilter = filter->mFilterSp; + // JNI Object + mFilter = filter; } -FilterCallback::~FilterCallback() { - JNIEnv *env = AndroidRuntime::getJNIEnv(); - if (mFilter != NULL) { - env->DeleteWeakGlobalRef(mFilter); - mFilter = NULL; - } -} +FilterCallback::~FilterCallback() {} /////////////// Filter /////////////////////// @@ -937,6 +996,77 @@ Return<void> FrontendCallback::onScanMessage(FrontendScanMessageType type, const return Void(); } +Return<void> FrontendCallback::onScanMessageExt1_1(FrontendScanMessageTypeExt1_1 type, + const FrontendScanMessageExt1_1& message) { + ALOGD("FrontendCallback::onScanMessageExt1_1, type=%d", type); + JNIEnv *env = AndroidRuntime::getJNIEnv(); + jclass clazz = env->FindClass("android/media/tv/tuner/Tuner"); + switch(type) { + case FrontendScanMessageTypeExt1_1::MODULATION: { + jint modulation = -1; + switch (message.modulation().getDiscriminator()) { + case FrontendModulation::hidl_discriminator::dvbc: { + modulation = (jint) message.modulation().dvbc(); + break; + } + case FrontendModulation::hidl_discriminator::dvbt: { + modulation = (jint) message.modulation().dvbt(); + break; + } + case FrontendModulation::hidl_discriminator::dvbs: { + modulation = (jint) message.modulation().dvbs(); + break; + } + case FrontendModulation::hidl_discriminator::isdbs: { + modulation = (jint) message.modulation().isdbs(); + break; + } + case FrontendModulation::hidl_discriminator::isdbs3: { + modulation = (jint) message.modulation().isdbs3(); + break; + } + case FrontendModulation::hidl_discriminator::isdbt: { + modulation = (jint) message.modulation().isdbt(); + break; + } + case FrontendModulation::hidl_discriminator::atsc: { + modulation = (jint) message.modulation().atsc(); + break; + } + case FrontendModulation::hidl_discriminator::atsc3: { + modulation = (jint) message.modulation().atsc3(); + break; + } + case FrontendModulation::hidl_discriminator::dtmb: { + modulation = (jint) message.modulation().dtmb(); + break; + } + default: { + break; + } + } + if (modulation > 0) { + env->CallVoidMethod( + mObject, + env->GetMethodID(clazz, "onModulationReported", "(I)V"), + modulation); + } + break; + } + case FrontendScanMessageTypeExt1_1::HIGH_PRIORITY: { + bool isHighPriority = message.isHighPriority(); + env->CallVoidMethod( + mObject, + env->GetMethodID(clazz, "onPriorityReported", "([B)V"), + isHighPriority); + break; + } + default: + break; + } + return Void(); +} + /////////////// Tuner /////////////////////// sp<ITuner> JTuner::mTuner; @@ -1034,6 +1164,7 @@ jobject JTuner::openFrontendById(int id) { return NULL; } mFe = fe; + mFe_1_1 = ::android::hardware::tv::tuner::V1_1::IFrontend::castFrom(mFe); mFeId = id; if (mDemux != NULL) { mDemux->setFrontendDataSource(mFeId); @@ -1166,6 +1297,33 @@ jobject JTuner::getIsdbtFrontendCaps(JNIEnv *env, FrontendInfo::FrontendCapabili guardIntervalCap); } +jobject JTuner::getDtmbFrontendCaps(JNIEnv *env, int id) { + jclass clazz = env->FindClass("android/media/tv/tuner/frontend/DtmbFrontendCapabilities"); + jmethodID capsInit = env->GetMethodID(clazz, "<init>", "(IIIIII)V"); + + if (mTuner_1_1 == NULL) { + ALOGD("1.1 Tuner is not found. Dtmb Frontend Caps are not supported."); + return NULL; + } + + Result result; + FrontendDtmbCapabilities dtmbCaps; + mTuner_1_1->getFrontendDtmbCapabilities(id, + [&](Result r, const FrontendDtmbCapabilities& caps) { + dtmbCaps = caps; + result = r; + }); + jint modulationCap = dtmbCaps.modulationCap; + jint transmissionModeCap = dtmbCaps.transmissionModeCap; + jint guardIntervalCap = dtmbCaps.guardIntervalCap; + jint interleaveModeCap = dtmbCaps.interleaveModeCap; + jint codeRateCap = dtmbCaps.codeRateCap; + jint bandwidthCap = dtmbCaps.bandwidthCap; + + return env->NewObject(clazz, capsInit, modulationCap, transmissionModeCap, guardIntervalCap, + interleaveModeCap, codeRateCap, bandwidthCap); +} + jobject JTuner::getFrontendInfo(int id) { FrontendInfo feInfo; Result res; @@ -1196,6 +1354,15 @@ jobject JTuner::getFrontendInfo(int id) { FrontendInfo::FrontendCapabilities caps = feInfo.frontendCaps; jobject jcaps = NULL; + + if (feInfo.type == static_cast<FrontendType>( + ::android::hardware::tv::tuner::V1_1::FrontendType::DTMB)) { + if (mTuner_1_1 == NULL) { + return NULL; + } + jcaps = getDtmbFrontendCaps(env, id); + } + switch(feInfo.type) { case FrontendType::ANALOG: if (FrontendInfo::FrontendCapabilities::hidl_discriminator::analogCaps @@ -1343,12 +1510,21 @@ jobject JTuner::openLnbByName(jstring name) { return lnbObj; } -int JTuner::tune(const FrontendSettings& settings) { +int JTuner::tune(const FrontendSettings& settings, const FrontendSettingsExt1_1& settingsExt1_1) { if (mFe == NULL) { ALOGE("frontend is not initialized"); return (int)Result::INVALID_STATE; } - Result result = mFe->tune(settings); + Result result; + sp<::android::hardware::tv::tuner::V1_1::IFrontend> fe_1_1 = + ::android::hardware::tv::tuner::V1_1::IFrontend::castFrom(mFe); + if (fe_1_1 == NULL) { + ALOGD("1.1 frontend is not found. Using 1.0 instead."); + result = mFe->tune(settings); + return (int)result; + } + + result = fe_1_1->tune_1_1(settings, settingsExt1_1); return (int)result; } @@ -1361,12 +1537,22 @@ int JTuner::stopTune() { return (int)result; } -int JTuner::scan(const FrontendSettings& settings, FrontendScanType scanType) { +int JTuner::scan(const FrontendSettings& settings, FrontendScanType scanType, + const FrontendSettingsExt1_1& settingsExt1_1) { if (mFe == NULL) { ALOGE("frontend is not initialized"); return (int)Result::INVALID_STATE; } - Result result = mFe->scan(settings, scanType); + Result result; + sp<::android::hardware::tv::tuner::V1_1::IFrontend> fe_1_1 = + ::android::hardware::tv::tuner::V1_1::IFrontend::castFrom(mFe); + if (fe_1_1 == NULL) { + ALOGD("1.1 frontend is not found. Using 1.0 instead."); + result = mFe->scan(settings, scanType); + return (int)result; + } + + result = fe_1_1->scan_1_1(settings, scanType, settingsExt1_1); return (int)result; } @@ -1493,6 +1679,27 @@ int JTuner::connectCiCam(jint id) { return (int) r; } +int JTuner::linkCiCam(int id) { + if (mFe_1_1 == NULL) { + ALOGE("frontend 1.1 is not initialized"); + return (int)Constant::INVALID_LTS_ID; + } + + Result res; + uint32_t ltsId; + mFe_1_1->linkCiCam(static_cast<uint32_t>(id), + [&](Result r, uint32_t id) { + res = r; + ltsId = id; + }); + + if (res != Result::SUCCESS) { + return (int)Constant::INVALID_LTS_ID; + } + + return (int) ltsId; +} + int JTuner::disconnectCiCam() { if (mDemux == NULL) { Result r = openDemux(); @@ -1504,6 +1711,18 @@ int JTuner::disconnectCiCam() { return (int) r; } + +int JTuner::unlinkCiCam(int id) { + if (mFe_1_1 == NULL) { + ALOGE("frontend 1.1 is not initialized"); + return (int)Result::INVALID_STATE; + } + + Result r = mFe_1_1->unlinkCiCam(static_cast<uint32_t>(id)); + + return (int) r; +} + jobject JTuner::openDescrambler() { ALOGD("JTuner::openDescrambler"); if (mTuner == nullptr || mDemux == nullptr) { @@ -1575,9 +1794,33 @@ jobject JTuner::openFilter(DemuxFilterType type, int bufferSize) { sp<Filter> filterSp = new Filter(iFilterSp, filterObj); filterSp->incStrong(filterObj); env->SetLongField(filterObj, gFields.filterContext, (jlong)filterSp.get()); - + filterSp->mIsMediaFilter = false; + filterSp->mAvSharedHandle = NULL; callback->setFilter(filterSp); + if (type.mainType == DemuxFilterMainType::MMTP) { + if (type.subType.mmtpFilterType() == DemuxMmtpFilterType::AUDIO || + type.subType.mmtpFilterType() == DemuxMmtpFilterType::VIDEO) { + filterSp->mIsMediaFilter = true; + } + } + + if (type.mainType == DemuxFilterMainType::TS) { + if (type.subType.tsFilterType() == DemuxTsFilterType::AUDIO || + type.subType.tsFilterType() == DemuxTsFilterType::VIDEO) { + filterSp->mIsMediaFilter = true; + } + } + + if (iFilterSp_1_1 != NULL && filterSp->mIsMediaFilter) { + iFilterSp_1_1->getAvSharedHandle([&](Result r, hidl_handle avMemory, uint64_t avMemSize) { + if (r == Result::SUCCESS) { + filterSp->mAvSharedHandle = native_handle_clone(avMemory.getNativeHandle()); + filterSp->mAvSharedMemSize = avMemSize; + } + }); + } + return filterObj; } @@ -1700,18 +1943,49 @@ jobject JTuner::getFrontendStatus(jintArray types) { } JNIEnv *env = AndroidRuntime::getJNIEnv(); jsize size = env->GetArrayLength(types); - std::vector<FrontendStatusType> v(size); - env->GetIntArrayRegion(types, 0, size, reinterpret_cast<jint*>(&v[0])); + jint intTypes[size]; + env->GetIntArrayRegion(types, 0, size, intTypes); + std::vector<FrontendStatusType> v; + std::vector<FrontendStatusTypeExt1_1> v_1_1; + for (int i = 0; i < size; i++) { + if (isV1_1ExtendedStatusType(intTypes[i])) { + v_1_1.push_back(static_cast<FrontendStatusTypeExt1_1>(intTypes[i])); + } else { + v.push_back(static_cast<FrontendStatusType>(intTypes[i])); + } + } Result res; hidl_vec<FrontendStatus> status; - mFe->getStatus(v, - [&](Result r, const hidl_vec<FrontendStatus>& s) { - res = r; - status = s; - }); - if (res != Result::SUCCESS) { - return NULL; + hidl_vec<FrontendStatusExt1_1> status_1_1; + + if (v.size() > 0) { + mFe->getStatus(v, + [&](Result r, const hidl_vec<FrontendStatus>& s) { + res = r; + status = s; + }); + if (res != Result::SUCCESS) { + return NULL; + } + } + + if (v_1_1.size() > 0) { + sp<::android::hardware::tv::tuner::V1_1::IFrontend> iFeSp_1_1; + iFeSp_1_1 = ::android::hardware::tv::tuner::V1_1::IFrontend::castFrom(mFe); + + if (iFeSp_1_1 != NULL) { + iFeSp_1_1->getStatusExt1_1(v_1_1, + [&](Result r, const hidl_vec<FrontendStatusExt1_1>& s) { + res = r; + status_1_1 = s; + }); + if (res != Result::SUCCESS) { + return NULL; + } + } else { + ALOGW("getStatusExt1_1 is not supported with the current HAL implementation."); + } } jclass clazz = env->FindClass("android/media/tv/tuner/frontend/FrontendStatus"); @@ -1939,14 +2213,283 @@ jobject JTuner::getFrontendStatus(jintArray types) { } } + for (auto s : status_1_1) { + switch(s.getDiscriminator()) { + case FrontendStatusExt1_1::hidl_discriminator::modulations: { + jfieldID field = env->GetFieldID(clazz, "mModulationsExt", "[I"); + std::vector<FrontendModulation> v = s.modulations(); + + jintArray valObj = env->NewIntArray(v.size()); + bool valid = false; + jint m[1]; + for (int i = 0; i < v.size(); i++) { + auto modulation = v[i]; + switch(modulation.getDiscriminator()) { + case FrontendModulation::hidl_discriminator::dvbc: { + m[0] = static_cast<jint>(modulation.dvbc()); + env->SetIntArrayRegion(valObj, i, 1, m); + valid = true; + break; + } + case FrontendModulation::hidl_discriminator::dvbs: { + m[0] = static_cast<jint>(modulation.dvbs()); + env->SetIntArrayRegion(valObj, i, 1, m); + valid = true; + break; + } + case FrontendModulation::hidl_discriminator::dvbt: { + m[0] = static_cast<jint>(modulation.dvbt()); + env->SetIntArrayRegion(valObj, i, 1, m); + valid = true; + break; + } + case FrontendModulation::hidl_discriminator::isdbs: { + m[0] = static_cast<jint>(modulation.isdbs()); + env->SetIntArrayRegion(valObj, i, 1, m); + valid = true; + break; + } + case FrontendModulation::hidl_discriminator::isdbs3: { + m[0] = static_cast<jint>(modulation.isdbs3()); + env->SetIntArrayRegion(valObj, i, 1, m); + valid = true; + break; + } + case FrontendModulation::hidl_discriminator::isdbt: { + m[0] = static_cast<jint>(modulation.isdbt()); + env->SetIntArrayRegion(valObj, i, 1, m); + valid = true; + break; + } + case FrontendModulation::hidl_discriminator::atsc: { + m[0] = static_cast<jint>(modulation.atsc()); + env->SetIntArrayRegion(valObj, i, 1, m); + valid = true; + break; + } + case FrontendModulation::hidl_discriminator::atsc3: { + m[0] = static_cast<jint>(modulation.atsc3()); + env->SetIntArrayRegion(valObj, i, 1, m); + valid = true; + break; + } + case FrontendModulation::hidl_discriminator::dtmb: { + m[0] = static_cast<jint>(modulation.dtmb()); + env->SetIntArrayRegion(valObj, i, 1, m); + valid = true; + break; + } + default: + break; + } + } + if (valid) { + env->SetObjectField(statusObj, field, valObj); + } + break; + } + case FrontendStatusExt1_1::hidl_discriminator::bers: { + jfieldID field = env->GetFieldID(clazz, "mBers", "[I"); + std::vector<uint32_t> v = s.bers(); + + jintArray valObj = env->NewIntArray(v.size()); + env->SetIntArrayRegion(valObj, 0, v.size(), reinterpret_cast<jint*>(&v[0])); + + env->SetObjectField(statusObj, field, valObj); + break; + } + case FrontendStatusExt1_1::hidl_discriminator::codeRates: { + jfieldID field = env->GetFieldID(clazz, "mCodeRates", "[I"); + std::vector<::android::hardware::tv::tuner::V1_1::FrontendInnerFec> v + = s.codeRates(); + + jintArray valObj = env->NewIntArray(v.size()); + env->SetIntArrayRegion(valObj, 0, v.size(), reinterpret_cast<jint*>(&v[0])); + + env->SetObjectField(statusObj, field, valObj); + break; + } + case FrontendStatusExt1_1::hidl_discriminator::bandwidth: { + jfieldID field = env->GetFieldID(clazz, "mBandwidth", "Ljava/lang/Integer;"); + auto bandwidth = s.bandwidth(); + jint intBandwidth; + bool valid = true; + switch(bandwidth.getDiscriminator()) { + case FrontendBandwidth::hidl_discriminator::atsc3: { + intBandwidth = static_cast<jint>(bandwidth.atsc3()); + break; + } + case FrontendBandwidth::hidl_discriminator::dvbt: { + intBandwidth = static_cast<jint>(bandwidth.dvbt()); + break; + } + case FrontendBandwidth::hidl_discriminator::isdbt: { + intBandwidth = static_cast<jint>(bandwidth.isdbt()); + break; + } + case FrontendBandwidth::hidl_discriminator::dtmb: { + intBandwidth = static_cast<jint>(bandwidth.dtmb()); + break; + } + default: + valid = false; + break; + } + if (valid) { + jobject newIntegerObj = env->NewObject(intClazz, initInt, intBandwidth); + env->SetObjectField(statusObj, field, newIntegerObj); + } + break; + } + case FrontendStatusExt1_1::hidl_discriminator::interval: { + jfieldID field = env->GetFieldID(clazz, "mGuardInterval", "Ljava/lang/Integer;"); + auto interval = s.interval(); + jint intInterval; + bool valid = true; + switch(interval.getDiscriminator()) { + case FrontendGuardInterval::hidl_discriminator::dvbt: { + intInterval = static_cast<jint>(interval.dvbt()); + break; + } + case FrontendGuardInterval::hidl_discriminator::isdbt: { + intInterval = static_cast<jint>(interval.isdbt()); + break; + } + case FrontendGuardInterval::hidl_discriminator::dtmb: { + intInterval = static_cast<jint>(interval.dtmb()); + break; + } + default: + valid = false; + break; + } + if (valid) { + jobject newIntegerObj = env->NewObject(intClazz, initInt, intInterval); + env->SetObjectField(statusObj, field, newIntegerObj); + } + break; + } + case FrontendStatusExt1_1::hidl_discriminator::transmissionMode: { + jfieldID field = env->GetFieldID(clazz, "mTransmissionMode", "Ljava/lang/Integer;"); + auto transmissionMode = s.transmissionMode(); + jint intTransmissionMode; + bool valid = true; + switch(transmissionMode.getDiscriminator()) { + case FrontendTransmissionMode::hidl_discriminator::dvbt: { + intTransmissionMode = static_cast<jint>(transmissionMode.dvbt()); + break; + } + case FrontendTransmissionMode::hidl_discriminator::isdbt: { + intTransmissionMode = static_cast<jint>(transmissionMode.isdbt()); + break; + } + case FrontendTransmissionMode::hidl_discriminator::dtmb: { + intTransmissionMode = static_cast<jint>(transmissionMode.dtmb()); + break; + } + default: + valid = false; + break; + } + if (valid) { + jobject newIntegerObj = env->NewObject(intClazz, initInt, intTransmissionMode); + env->SetObjectField(statusObj, field, newIntegerObj); + } + break; + } + case FrontendStatusExt1_1::hidl_discriminator::uec: { + jfieldID field = env->GetFieldID(clazz, "mUec", "Ljava/lang/Integer;"); + jobject newIntegerObj = env->NewObject( + intClazz, initInt, static_cast<jint>(s.uec())); + env->SetObjectField(statusObj, field, newIntegerObj); + break; + } + case FrontendStatusExt1_1::hidl_discriminator::systemId: { + jfieldID field = env->GetFieldID(clazz, "mSystemId", "Ljava/lang/Integer;"); + jobject newIntegerObj = env->NewObject( + intClazz, initInt, static_cast<jint>(s.systemId())); + env->SetObjectField(statusObj, field, newIntegerObj); + break; + } + case FrontendStatusExt1_1::hidl_discriminator::interleaving: { + jfieldID field = env->GetFieldID(clazz, "mInterleaving", "[I"); + + std::vector<FrontendInterleaveMode> v = s.interleaving(); + jintArray valObj = env->NewIntArray(v.size()); + bool valid = false; + jint in[1]; + for (int i = 0; i < v.size(); i++) { + auto interleaving = v[i]; + switch(interleaving.getDiscriminator()) { + case FrontendInterleaveMode::hidl_discriminator::atsc3: { + in[0] = static_cast<jint>(interleaving.atsc3()); + env->SetIntArrayRegion(valObj, i, 1, in); + valid = true; + break; + } + case FrontendInterleaveMode::hidl_discriminator::dvbc: { + in[0] = static_cast<jint>(interleaving.dvbc()); + env->SetIntArrayRegion(valObj, i, 1, in); + valid = true; + break; + } + case FrontendInterleaveMode::hidl_discriminator::dtmb: { + in[0] = static_cast<jint>(interleaving.dtmb()); + env->SetIntArrayRegion(valObj, i, 1, in); + valid = true; + break; + } + default: + break; + } + } + if (valid) { + env->SetObjectField(statusObj, field, valObj); + } + break; + } + case FrontendStatusExt1_1::hidl_discriminator::isdbtSegment: { + jfieldID field = env->GetFieldID(clazz, "mIsdbtSegment", "[I"); + std::vector<uint8_t> v = s.isdbtSegment(); + + jintArray valObj = env->NewIntArray(v.size()); + env->SetIntArrayRegion(valObj, 0, v.size(), reinterpret_cast<jint*>(&v[0])); + + env->SetObjectField(statusObj, field, valObj); + break; + } + case FrontendStatusExt1_1::hidl_discriminator::tsDataRate: { + jfieldID field = env->GetFieldID(clazz, "mTsDataRate", "[I"); + std::vector<uint32_t> v = s.tsDataRate(); + + jintArray valObj = env->NewIntArray(v.size()); + env->SetIntArrayRegion(valObj, 0, v.size(), reinterpret_cast<jint*>(&v[0])); + + env->SetObjectField(statusObj, field, valObj); + break; + } + default: { + break; + } + } + } return statusObj; } +bool JTuner::isV1_1ExtendedStatusType(int type) { + return (type > static_cast<int>(FrontendStatusType::ATSC3_PLP_INFO) + && type <= static_cast<int>(FrontendStatusTypeExt1_1::TS_DATA_RATES)); +} + jint JTuner::closeFrontend() { Result r = Result::SUCCESS; if (mFe != NULL) { r = mFe->close(); } + if (r == Result::SUCCESS) { + mFe = NULL; + mFe_1_1 = NULL; + } return (jint) r; } @@ -1955,6 +2498,9 @@ jint JTuner::closeDemux() { if (mDemux != NULL) { r = mDemux->close(); } + if (r == Result::SUCCESS) { + mDemux = NULL; + } return (jint) r; } @@ -2007,6 +2553,22 @@ static uint32_t getFrontendSettingsFreq(JNIEnv *env, const jobject& settings) { return freq; } +static uint32_t getFrontendSettingsEndFreq(JNIEnv *env, const jobject& settings) { + jclass clazz = env->FindClass("android/media/tv/tuner/frontend/FrontendSettings"); + jfieldID endFreqField = env->GetFieldID(clazz, "mEndFrequency", "I"); + uint32_t endFreq = static_cast<uint32_t>(env->GetIntField(settings, endFreqField)); + return endFreq; +} + +static FrontendSpectralInversion getFrontendSettingsSpectralInversion( + JNIEnv *env, const jobject& settings) { + jclass clazz = env->FindClass("android/media/tv/tuner/frontend/FrontendSettings"); + jfieldID inversionField = env->GetFieldID(clazz, "mSpectralInversion", "I"); + FrontendSpectralInversion inversion = + static_cast<FrontendSpectralInversion>(env->GetIntField(settings, inversionField)); + return inversion; +} + static FrontendSettings getAnalogFrontendSettings(JNIEnv *env, const jobject& settings) { FrontendSettings frontendSettings; uint32_t freq = getFrontendSettingsFreq(env, settings); @@ -2026,6 +2588,18 @@ static FrontendSettings getAnalogFrontendSettings(JNIEnv *env, const jobject& se return frontendSettings; } +static void getAnalogFrontendSettingsExt1_1(JNIEnv *env, const jobject& settings, + FrontendSettingsExt1_1& settingsExt1_1) { + jclass clazz = env->FindClass("android/media/tv/tuner/frontend/AnalogFrontendSettings"); + FrontendAnalogAftFlag aftFlag = + static_cast<FrontendAnalogAftFlag>( + env->GetIntField(settings, env->GetFieldID(clazz, "mAftFlag", "I"))); + FrontendAnalogSettingsExt1_1 analogExt1_1 { + .aftFlag = aftFlag, + }; + settingsExt1_1.settingExt.analog(analogExt1_1); +} + static hidl_vec<FrontendAtsc3PlpSettings> getAtsc3PlpSettings( JNIEnv *env, const jobject& settings) { jclass clazz = env->FindClass("android/media/tv/tuner/frontend/Atsc3FrontendSettings"); @@ -2145,6 +2719,19 @@ static FrontendSettings getDvbcFrontendSettings(JNIEnv *env, const jobject& sett return frontendSettings; } +static void getDvbcFrontendSettingsExt1_1(JNIEnv *env, const jobject& settings, + FrontendSettingsExt1_1& settingsExt1_1) { + jclass clazz = env->FindClass("android/media/tv/tuner/frontend/DvbcFrontendSettings"); + FrontendCableTimeInterleaveMode interleaveMode = + static_cast<FrontendCableTimeInterleaveMode>( + env->GetIntField(settings, env->GetFieldID(clazz, "mInterleaveMode", "I"))); + + FrontendDvbcSettingsExt1_1 dvbcExt1_1 { + .interleaveMode = interleaveMode, + }; + settingsExt1_1.settingExt.dvbc(dvbcExt1_1); +} + static FrontendDvbsCodeRate getDvbsCodeRate(JNIEnv *env, const jobject& settings) { jclass clazz = env->FindClass("android/media/tv/tuner/frontend/DvbsFrontendSettings"); jobject jcodeRate = @@ -2186,7 +2773,6 @@ static FrontendSettings getDvbsFrontendSettings(JNIEnv *env, const jobject& sett uint32_t freq = getFrontendSettingsFreq(env, settings); jclass clazz = env->FindClass("android/media/tv/tuner/frontend/DvbsFrontendSettings"); - FrontendDvbsModulation modulation = static_cast<FrontendDvbsModulation>( env->GetIntField(settings, env->GetFieldID(clazz, "mModulation", "I"))); @@ -2225,6 +2811,22 @@ static FrontendSettings getDvbsFrontendSettings(JNIEnv *env, const jobject& sett return frontendSettings; } +static void getDvbsFrontendSettingsExt1_1(JNIEnv *env, const jobject& settings, + FrontendSettingsExt1_1& settingsExt1_1) { + jclass clazz = env->FindClass("android/media/tv/tuner/frontend/DvbsFrontendSettings"); + FrontendDvbsScanType scanType = + static_cast<FrontendDvbsScanType>( + env->GetIntField(settings, env->GetFieldID(clazz, "mScanType", "I"))); + bool isDiseqcRxMessage = static_cast<bool>(env->GetBooleanField( + settings, env->GetFieldID(clazz, "mIsDiseqcRxMessage", "B"))); + + FrontendDvbsSettingsExt1_1 dvbsExt1_1 { + .scanType = scanType, + .isDiseqcRxMessage = isDiseqcRxMessage, + }; + settingsExt1_1.settingExt.dvbs(dvbsExt1_1); +} + static FrontendSettings getDvbtFrontendSettings(JNIEnv *env, const jobject& settings) { FrontendSettings frontendSettings; uint32_t freq = getFrontendSettingsFreq(env, settings); @@ -2291,6 +2893,25 @@ static FrontendSettings getDvbtFrontendSettings(JNIEnv *env, const jobject& sett return frontendSettings; } +static void getDvbtFrontendSettingsExt1_1(JNIEnv *env, const jobject& settings, + FrontendSettingsExt1_1& settingsExt1_1) { + jclass clazz = env->FindClass("android/media/tv/tuner/frontend/DvbtFrontendSettings"); + + FrontendDvbtSettingsExt1_1 dvbtExt1_1; + int transmissionMode = + env->GetIntField(settings, env->GetFieldID(clazz, "mTransmissionMode", "I")); + dvbtExt1_1.transmissionMode = static_cast< + ::android::hardware::tv::tuner::V1_1::FrontendDvbtTransmissionMode>( + transmissionMode); + + int constellation = + env->GetIntField(settings, env->GetFieldID(clazz, "mConstellation", "I")); + dvbtExt1_1.constellation = static_cast< + ::android::hardware::tv::tuner::V1_1::FrontendDvbtConstellation>(constellation); + + settingsExt1_1.settingExt.dvbt(dvbtExt1_1); +} + static FrontendSettings getIsdbsFrontendSettings(JNIEnv *env, const jobject& settings) { FrontendSettings frontendSettings; uint32_t freq = getFrontendSettingsFreq(env, settings); @@ -2399,6 +3020,41 @@ static FrontendSettings getIsdbtFrontendSettings(JNIEnv *env, const jobject& set return frontendSettings; } +static void getDtmbFrontendSettings(JNIEnv *env, const jobject& settings, + FrontendSettingsExt1_1& settingsExt1_1) { + uint32_t freq = getFrontendSettingsFreq(env, settings); + jclass clazz = env->FindClass("android/media/tv/tuner/frontend/DtmbFrontendSettings"); + FrontendDtmbModulation modulation = + static_cast<FrontendDtmbModulation>( + env->GetIntField(settings, env->GetFieldID(clazz, "mModulation", "I"))); + FrontendDtmbBandwidth bandwidth = + static_cast<FrontendDtmbBandwidth>( + env->GetIntField(settings, env->GetFieldID(clazz, "mBandwidth", "I"))); + FrontendDtmbTransmissionMode transmissionMode = + static_cast<FrontendDtmbTransmissionMode>( + env->GetIntField(settings, env->GetFieldID(clazz, "mTransmissionMode", "I"))); + FrontendDtmbCodeRate codeRate = + static_cast<FrontendDtmbCodeRate>( + env->GetIntField(settings, env->GetFieldID(clazz, "mCodeRate", "I"))); + FrontendDtmbGuardInterval guardInterval = + static_cast<FrontendDtmbGuardInterval>( + env->GetIntField(settings, env->GetFieldID(clazz, "mGuardInterval", "I"))); + FrontendDtmbTimeInterleaveMode interleaveMode = + static_cast<FrontendDtmbTimeInterleaveMode>( + env->GetIntField(settings, env->GetFieldID(clazz, "mTimeInterleaveMode", "I"))); + + FrontendDtmbSettings frontendDtmbSettings { + .frequency = freq, + .modulation = modulation, + .bandwidth = bandwidth, + .transmissionMode = transmissionMode, + .codeRate = codeRate, + .guardInterval = guardInterval, + .interleaveMode = interleaveMode, + }; + settingsExt1_1.settingExt.dtmb(frontendDtmbSettings); +} + static FrontendSettings getFrontendSettings(JNIEnv *env, int type, jobject settings) { ALOGD("getFrontendSettings %d", type); @@ -2431,6 +3087,59 @@ static FrontendSettings getFrontendSettings(JNIEnv *env, int type, jobject setti } } +static FrontendSettingsExt1_1 getFrontendSettingsExt1_1(JNIEnv *env, int type, jobject settings) { + ALOGD("getFrontendSettingsExt1_1 %d", type); + + FrontendSettingsExt1_1 settingsExt1_1 { + .endFrequency = static_cast<uint32_t>(Constant::INVALID_FRONTEND_SETTING_FREQUENCY), + .inversion = FrontendSpectralInversion::UNDEFINED, + }; + settingsExt1_1.settingExt.noinit(); + + if (type == static_cast<int>(::android::hardware::tv::tuner::V1_1::FrontendType::DTMB)) { + getDtmbFrontendSettings(env, settings, settingsExt1_1); + } else { + FrontendType feType = static_cast<FrontendType>(type); + switch(feType) { + case FrontendType::DVBS: + getDvbsFrontendSettingsExt1_1(env, settings, settingsExt1_1); + break; + case FrontendType::DVBT: + getDvbtFrontendSettingsExt1_1(env, settings, settingsExt1_1); + break; + case FrontendType::ANALOG: + getAnalogFrontendSettingsExt1_1(env, settings, settingsExt1_1); + break; + case FrontendType::ATSC3: + break; + case FrontendType::ATSC: + break; + case FrontendType::DVBC: + getDvbcFrontendSettingsExt1_1(env, settings, settingsExt1_1); + break; + case FrontendType::ISDBS: + break; + case FrontendType::ISDBS3: + break; + case FrontendType::ISDBT: + break; + default: + // should never happen because a type is associated with a subclass of + // FrontendSettings and not set by users + jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", + "Unsupported frontend type %d", type); + return FrontendSettingsExt1_1(); + } + } + + uint32_t endFreq = getFrontendSettingsEndFreq(env, settings); + FrontendSpectralInversion inversion = getFrontendSettingsSpectralInversion(env, settings); + settingsExt1_1.endFrequency = endFreq; + settingsExt1_1.inversion = inversion; + + return settingsExt1_1; +} + static sp<Filter> getFilter(JNIEnv *env, jobject filter) { return (Filter *)env->GetLongField(filter, gFields.filterContext); } @@ -2572,7 +3281,9 @@ static jint android_media_tv_Tuner_close_frontend_by_handle( static int android_media_tv_Tuner_tune(JNIEnv *env, jobject thiz, jint type, jobject settings) { sp<JTuner> tuner = getTuner(env, thiz); - return tuner->tune(getFrontendSettings(env, type, settings)); + FrontendSettings setting = getFrontendSettings(env, type, settings); + FrontendSettingsExt1_1 settingExt = getFrontendSettingsExt1_1(env, type, settings); + return tuner->tune(setting, settingExt); } static int android_media_tv_Tuner_stop_tune(JNIEnv *env, jobject thiz) { @@ -2583,8 +3294,9 @@ static int android_media_tv_Tuner_stop_tune(JNIEnv *env, jobject thiz) { static int android_media_tv_Tuner_scan( JNIEnv *env, jobject thiz, jint settingsType, jobject settings, jint scanType) { sp<JTuner> tuner = getTuner(env, thiz); - return tuner->scan(getFrontendSettings( - env, settingsType, settings), static_cast<FrontendScanType>(scanType)); + FrontendSettings setting = getFrontendSettings(env, settingsType, settings); + FrontendSettingsExt1_1 settingExt = getFrontendSettingsExt1_1(env, settingsType, settings); + return tuner->scan(setting, static_cast<FrontendScanType>(scanType), settingExt); } static int android_media_tv_Tuner_stop_scan(JNIEnv *env, jobject thiz) { @@ -2629,11 +3341,21 @@ static int android_media_tv_Tuner_connect_cicam(JNIEnv *env, jobject thiz, jint return tuner->connectCiCam(id); } +static int android_media_tv_Tuner_link_cicam(JNIEnv *env, jobject thiz, jint id) { + sp<JTuner> tuner = getTuner(env, thiz); + return tuner->linkCiCam(id); +} + static int android_media_tv_Tuner_disconnect_cicam(JNIEnv *env, jobject thiz) { sp<JTuner> tuner = getTuner(env, thiz); return tuner->disconnectCiCam(); } +static int android_media_tv_Tuner_unlink_cicam(JNIEnv *env, jobject thiz, jint id) { + sp<JTuner> tuner = getTuner(env, thiz); + return tuner->unlinkCiCam(id); +} + static jobject android_media_tv_Tuner_get_frontend_info(JNIEnv *env, jobject thiz, jint id) { sp<JTuner> tuner = getTuner(env, thiz); return tuner->getFrontendInfo(id); @@ -2769,6 +3491,31 @@ static DemuxFilterAvSettings getFilterAvSettings(JNIEnv *env, const jobject& set return filterAvSettings; } +static bool getAvStreamType(JNIEnv *env, jobject filterConfigObj, AvStreamType& type) { + jobject settingsObj = + env->GetObjectField( + filterConfigObj, + env->GetFieldID( + env->FindClass("android/media/tv/tuner/filter/FilterConfiguration"), + "mSettings", + "Landroid/media/tv/tuner/filter/Settings;")); + jclass clazz = env->FindClass("android/media/tv/tuner/filter/AvSettings"); + AvStreamType streamType; + AudioStreamType audioStreamType = static_cast<AudioStreamType>( + env->GetIntField(settingsObj, env->GetFieldID(clazz, "mAudioStreamType", "I"))); + if (audioStreamType != AudioStreamType::UNDEFINED) { + type.audio(audioStreamType); + return true; + } + VideoStreamType videoStreamType = static_cast<VideoStreamType>( + env->GetIntField(settingsObj, env->GetFieldID(clazz, "mVideoStreamType", "I"))); + if (videoStreamType != VideoStreamType::UNDEFINED) { + type.video(videoStreamType); + return true; + } + return false; +} + static DemuxFilterPesDataSettings getFilterPesDataSettings(JNIEnv *env, const jobject& settings) { jclass clazz = env->FindClass("android/media/tv/tuner/filter/PesSettings"); uint16_t streamId = static_cast<uint16_t>( @@ -3041,6 +3788,29 @@ static DemuxFilterSettings getFilterConfiguration( return filterSettings; } +static Result configureIpFilterContextId( + JNIEnv *env, sp<IFilter> iFilterSp, jobject ipFilterConfigObj) { + jclass clazz = env->FindClass( + "android/media/tv/tuner/filter/IpFilterConfiguration"); + uint32_t cid = env->GetIntField(ipFilterConfigObj, env->GetFieldID( + clazz, "mIpFilterContextId", "I")); + Result res = Result::SUCCESS; + if (cid != static_cast<uint32_t>(Constant::INVALID_IP_FILTER_CONTEXT_ID)) { + sp<::android::hardware::tv::tuner::V1_1::IFilter> iFilterSp_1_1; + iFilterSp_1_1 = ::android::hardware::tv::tuner::V1_1::IFilter::castFrom(iFilterSp); + + if (iFilterSp_1_1 != NULL) { + res = iFilterSp_1_1->configureIpCid(cid); + if (res != Result::SUCCESS) { + return res; + } + } else { + ALOGW("configureIpCid is not supported with the current HAL implementation."); + } + } + return res; +} + static jint copyData(JNIEnv *env, std::unique_ptr<MQ>& mq, EventFlag* flag, jbyteArray buffer, jlong offset, jlong size) { ALOGD("copyData, size=%ld, offset=%ld", (long) size, (long) offset); @@ -3068,6 +3838,16 @@ static jint copyData(JNIEnv *env, std::unique_ptr<MQ>& mq, EventFlag* flag, jbyt return size; } +static bool isAvFilterSettings(DemuxFilterSettings filterSettings) { + return (filterSettings.getDiscriminator() == DemuxFilterSettings::hidl_discriminator::ts + && filterSettings.ts().filterSettings.getDiscriminator() + == DemuxTsFilterSettings::FilterSettings::hidl_discriminator::av) + || + (filterSettings.getDiscriminator() == DemuxFilterSettings::hidl_discriminator::mmtp + && filterSettings.mmtp().filterSettings.getDiscriminator() + == DemuxMmtpFilterSettings::FilterSettings::hidl_discriminator::av); +} + static jint android_media_tv_Tuner_configure_filter( JNIEnv *env, jobject filter, int type, int subtype, jobject settings) { ALOGD("configure filter type=%d, subtype=%d", type, subtype); @@ -3084,6 +3864,27 @@ static jint android_media_tv_Tuner_configure_filter( return (jint) res; } + if (static_cast<DemuxFilterMainType>(type) == DemuxFilterMainType::IP) { + res = configureIpFilterContextId(env, iFilterSp, settings); + if (res != Result::SUCCESS) { + return (jint) res; + } + } + + AvStreamType streamType; + if (isAvFilterSettings(filterSettings) && getAvStreamType(env, settings, streamType)) { + sp<::android::hardware::tv::tuner::V1_1::IFilter> iFilterSp_1_1; + iFilterSp_1_1 = ::android::hardware::tv::tuner::V1_1::IFilter::castFrom(iFilterSp); + if (iFilterSp_1_1 != NULL) { + res = iFilterSp_1_1->configureAvStreamType(streamType); + } else { + ALOGW("configureAvStreamType is not supported with the current HAL implementation."); + } + if (res != Result::SUCCESS) { + return (jint) res; + } + } + MQDescriptorSync<uint8_t> filterMQDesc; Result getQueueDescResult = Result::UNKNOWN_ERROR; if (filterSp->mFilterMQ == NULL) { @@ -3786,6 +4587,10 @@ static const JNINativeMethod gTunerMethods[] = { { "nativeGetAvSyncTime", "(I)Ljava/lang/Long;", (void *)android_media_tv_Tuner_get_av_sync_time }, { "nativeConnectCiCam", "(I)I", (void *)android_media_tv_Tuner_connect_cicam }, + { "nativeLinkCiCam", "(I)I", + (void *)android_media_tv_Tuner_link_cicam }, + { "nativeUnlinkCiCam", "(I)I", + (void *)android_media_tv_Tuner_unlink_cicam }, { "nativeDisconnectCiCam", "()I", (void *)android_media_tv_Tuner_disconnect_cicam }, { "nativeGetFrontendInfo", "(I)Landroid/media/tv/tuner/frontend/FrontendInfo;", (void *)android_media_tv_Tuner_get_frontend_info }, diff --git a/media/jni/android_media_tv_Tuner.h b/media/jni/android_media_tv_Tuner.h index d7dc600a166b..71d2983b7c51 100644 --- a/media/jni/android_media_tv_Tuner.h +++ b/media/jni/android_media_tv_Tuner.h @@ -19,6 +19,8 @@ #include <android/hardware/tv/tuner/1.1/IFilter.h> #include <android/hardware/tv/tuner/1.1/IFilterCallback.h> +#include <android/hardware/tv/tuner/1.1/IFrontend.h> +#include <android/hardware/tv/tuner/1.1/IFrontendCallback.h> #include <android/hardware/tv/tuner/1.1/ITuner.h> #include <android/hardware/tv/tuner/1.1/types.h> @@ -54,6 +56,7 @@ using ::android::hardware::tv::tuner::V1_0::FrontendScanMessage; using ::android::hardware::tv::tuner::V1_0::FrontendScanMessageType; using ::android::hardware::tv::tuner::V1_0::FrontendScanType; using ::android::hardware::tv::tuner::V1_0::FrontendSettings; +using ::android::hardware::tv::tuner::V1_1::FrontendSettingsExt1_1; using ::android::hardware::tv::tuner::V1_0::IDemux; using ::android::hardware::tv::tuner::V1_0::IDescrambler; using ::android::hardware::tv::tuner::V1_0::IDvr; @@ -61,7 +64,6 @@ using ::android::hardware::tv::tuner::V1_0::IDvrCallback; using ::android::hardware::tv::tuner::V1_0::IFilter; using ::android::hardware::tv::tuner::V1_1::IFilterCallback; using ::android::hardware::tv::tuner::V1_0::IFrontend; -using ::android::hardware::tv::tuner::V1_0::IFrontendCallback; using ::android::hardware::tv::tuner::V1_0::ILnb; using ::android::hardware::tv::tuner::V1_0::ILnbCallback; using ::android::hardware::tv::tuner::V1_0::ITimeFilter; @@ -71,6 +73,9 @@ using ::android::hardware::tv::tuner::V1_0::LnbId; using ::android::hardware::tv::tuner::V1_0::PlaybackStatus; using ::android::hardware::tv::tuner::V1_0::RecordStatus; using ::android::hardware::tv::tuner::V1_0::Result; +using ::android::hardware::tv::tuner::V1_1::FrontendScanMessageExt1_1; +using ::android::hardware::tv::tuner::V1_1::FrontendScanMessageTypeExt1_1; +using ::android::hardware::tv::tuner::V1_1::IFrontendCallback; using MQ = MessageQueue<uint8_t, kSynchronizedReadWrite>; @@ -117,18 +122,32 @@ struct Dvr : public RefBase { int mFd; }; +struct Filter : public RefBase { + Filter(sp<IFilter> sp, jobject obj); + ~Filter(); + int close(); + sp<IFilter> getIFilter(); + sp<IFilter> mFilterSp; + std::unique_ptr<MQ> mFilterMQ; + EventFlag* mFilterMQEventFlag; + jweak mFilterObj; + native_handle_t* mAvSharedHandle; + uint64_t mAvSharedMemSize; + bool mIsMediaFilter; +}; + struct MediaEvent : public RefBase { - MediaEvent(sp<IFilter> iFilter, hidl_handle avHandle, uint64_t dataId, - uint64_t dataLength, jobject obj); + MediaEvent(sp<Filter> filter, hidl_handle avHandle, uint64_t dataId, + uint64_t dataSize, jobject obj); ~MediaEvent(); jobject getLinearBlock(); uint64_t getAudioHandle(); void finalize(); - sp<IFilter> mIFilter; + sp<Filter> mFilter; native_handle_t* mAvHandle; uint64_t mDataId; - uint64_t mDataLength; + uint64_t mDataSize; uint8_t* mBuffer; android::Mutex mLock; int mDataIdRefCnt; @@ -139,17 +158,6 @@ struct MediaEvent : public RefBase { std::weak_ptr<C2Buffer> mC2Buffer; }; -struct Filter : public RefBase { - Filter(sp<IFilter> sp, jobject obj); - ~Filter(); - int close(); - sp<IFilter> getIFilter(); - sp<IFilter> mFilterSp; - std::unique_ptr<MQ> mFilterMQ; - EventFlag* mFilterMQEventFlag; - jweak mFilterObj; -}; - struct FilterCallback : public IFilterCallback { ~FilterCallback(); virtual Return<void> onFilterEvent_1_1(const DemuxFilterEvent& filterEvent, @@ -159,8 +167,7 @@ struct FilterCallback : public IFilterCallback { void setFilter(const sp<Filter> filter); private: - jweak mFilter; - sp<IFilter> mIFilter; + sp<Filter> mFilter; jobjectArray getSectionEvent( jobjectArray& arr, const std::vector<DemuxFilterEvent::Event>& events); jobjectArray getMediaEvent( @@ -187,6 +194,8 @@ struct FrontendCallback : public IFrontendCallback { virtual Return<void> onEvent(FrontendEventType frontendEventType); virtual Return<void> onScanMessage( FrontendScanMessageType type, const FrontendScanMessage& message); + virtual Return<void> onScanMessageExt1_1( + FrontendScanMessageTypeExt1_1 type, const FrontendScanMessageExt1_1& messageExt); jweak mObject; FrontendId mId; @@ -207,14 +216,17 @@ struct JTuner : public RefBase { jobject getAvSyncHwId(sp<Filter> filter); jobject getAvSyncTime(jint id); int connectCiCam(jint id); + int linkCiCam(jint id); int disconnectCiCam(); + int unlinkCiCam(jint id); jobject getFrontendIds(); jobject openFrontendById(int id); jint closeFrontendById(int id); jobject getFrontendInfo(int id); - int tune(const FrontendSettings& settings); + int tune(const FrontendSettings& settings, const FrontendSettingsExt1_1& settingsExt1_1); int stopTune(); - int scan(const FrontendSettings& settings, FrontendScanType scanType); + int scan(const FrontendSettings& settings, FrontendScanType scanType, + const FrontendSettingsExt1_1& settingsExt1_1); int stopScan(); int setLnb(int id); int setLna(bool enable); @@ -245,6 +257,7 @@ private: static int mTunerVersion; hidl_vec<FrontendId> mFeIds; sp<IFrontend> mFe; + sp<::android::hardware::tv::tuner::V1_1::IFrontend> mFe_1_1; int mFeId; hidl_vec<LnbId> mLnbIds; sp<ILnb> mLnb; @@ -259,6 +272,9 @@ private: static jobject getIsdbs3FrontendCaps(JNIEnv *env, FrontendInfo::FrontendCapabilities& caps); static jobject getIsdbsFrontendCaps(JNIEnv *env, FrontendInfo::FrontendCapabilities& caps); static jobject getIsdbtFrontendCaps(JNIEnv *env, FrontendInfo::FrontendCapabilities& caps); + static jobject getDtmbFrontendCaps(JNIEnv *env, int id); + + bool isV1_1ExtendedStatusType(jint type); }; class C2DataIdInfo : public C2Param { diff --git a/media/tests/AudioPolicyTest/AndroidManifest.xml b/media/tests/AudioPolicyTest/AndroidManifest.xml index adb058c82870..a7ab8286cdca 100644 --- a/media/tests/AudioPolicyTest/AndroidManifest.xml +++ b/media/tests/AudioPolicyTest/AndroidManifest.xml @@ -25,7 +25,7 @@ <application> <uses-library android:name="android.test.runner" /> <activity android:label="@string/app_name" android:name="AudioPolicyTest" - android:screenOrientation="landscape"> + android:screenOrientation="landscape" android:exported="true"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER"/> diff --git a/native/graphics/jni/imagedecoder.cpp b/native/graphics/jni/imagedecoder.cpp index 23035b69af90..ac4c16a4199b 100644 --- a/native/graphics/jni/imagedecoder.cpp +++ b/native/graphics/jni/imagedecoder.cpp @@ -353,3 +353,18 @@ bool AImageDecoder_isAnimated(AImageDecoder* decoder) { ImageDecoder* imageDecoder = toDecoder(decoder); return imageDecoder->mCodec->codec()->getFrameCount() > 1; } + +int32_t AImageDecoder_getRepeatCount(AImageDecoder* decoder) { + if (!decoder) return ANDROID_IMAGE_DECODER_BAD_PARAMETER; + + ImageDecoder* imageDecoder = toDecoder(decoder); + const int count = imageDecoder->mCodec->codec()->getRepetitionCount(); + + // Skia should not report anything out of range, but defensively treat + // negative and too big as INFINITE. + if (count == SkCodec::kRepetitionCountInfinite || count < 0 + || count > std::numeric_limits<int32_t>::max()) { + return ANDROID_IMAGE_DECODER_INFINITE; + } + return count; +} diff --git a/native/graphics/jni/libjnigraphics.map.txt b/native/graphics/jni/libjnigraphics.map.txt index af2c455e36df..a184ab9d42e9 100644 --- a/native/graphics/jni/libjnigraphics.map.txt +++ b/native/graphics/jni/libjnigraphics.map.txt @@ -14,6 +14,7 @@ LIBJNIGRAPHICS { AImageDecoder_computeSampledSize; # introduced=30 AImageDecoder_setCrop; # introduced=30 AImageDecoder_isAnimated; # introduced=31 + AImageDecoder_getRepeatCount; # introduced=31 AImageDecoderHeaderInfo_getWidth; # introduced=30 AImageDecoderHeaderInfo_getHeight; # introduced=30 AImageDecoderHeaderInfo_getMimeType; # introduced=30 diff --git a/non-updatable-api/current.txt b/non-updatable-api/current.txt index 8596f87498b1..225a2e70e998 100644 --- a/non-updatable-api/current.txt +++ b/non-updatable-api/current.txt @@ -1613,6 +1613,7 @@ package android { field public static final int windowHideAnimation = 16842935; // 0x10100b7 field public static final int windowIsFloating = 16842839; // 0x1010057 field public static final int windowIsTranslucent = 16842840; // 0x1010058 + field public static final int windowLayoutAffinity = 16844313; // 0x1010619 field public static final int windowLayoutInDisplayCutoutMode = 16844166; // 0x1010586 field public static final int windowLightNavigationBar = 16844140; // 0x101056c field public static final int windowLightStatusBar = 16844000; // 0x10104e0 @@ -2850,6 +2851,7 @@ package android.accessibilityservice { method public int describeContents(); method public int getDisplayId(); method public int getGestureId(); + method @NonNull public java.util.List<android.view.MotionEvent> getMotionEvents(); method public void writeToParcel(@NonNull android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.accessibilityservice.AccessibilityGestureEvent> CREATOR; } @@ -2889,6 +2891,7 @@ package android.accessibilityservice { field public static final int GESTURE_2_FINGER_DOUBLE_TAP = 20; // 0x14 field public static final int GESTURE_2_FINGER_DOUBLE_TAP_AND_HOLD = 40; // 0x28 field public static final int GESTURE_2_FINGER_SINGLE_TAP = 19; // 0x13 + field public static final int GESTURE_2_FINGER_SINGLE_TAP_AND_HOLD = 43; // 0x2b field public static final int GESTURE_2_FINGER_SWIPE_DOWN = 26; // 0x1a field public static final int GESTURE_2_FINGER_SWIPE_LEFT = 27; // 0x1b field public static final int GESTURE_2_FINGER_SWIPE_RIGHT = 28; // 0x1c @@ -2897,11 +2900,13 @@ package android.accessibilityservice { field public static final int GESTURE_3_FINGER_DOUBLE_TAP = 23; // 0x17 field public static final int GESTURE_3_FINGER_DOUBLE_TAP_AND_HOLD = 41; // 0x29 field public static final int GESTURE_3_FINGER_SINGLE_TAP = 22; // 0x16 + field public static final int GESTURE_3_FINGER_SINGLE_TAP_AND_HOLD = 44; // 0x2c field public static final int GESTURE_3_FINGER_SWIPE_DOWN = 30; // 0x1e field public static final int GESTURE_3_FINGER_SWIPE_LEFT = 31; // 0x1f field public static final int GESTURE_3_FINGER_SWIPE_RIGHT = 32; // 0x20 field public static final int GESTURE_3_FINGER_SWIPE_UP = 29; // 0x1d field public static final int GESTURE_3_FINGER_TRIPLE_TAP = 24; // 0x18 + field public static final int GESTURE_3_FINGER_TRIPLE_TAP_AND_HOLD = 45; // 0x2d field public static final int GESTURE_4_FINGER_DOUBLE_TAP = 38; // 0x26 field public static final int GESTURE_4_FINGER_DOUBLE_TAP_AND_HOLD = 42; // 0x2a field public static final int GESTURE_4_FINGER_SINGLE_TAP = 37; // 0x25 @@ -2928,6 +2933,7 @@ package android.accessibilityservice { field public static final int GESTURE_SWIPE_UP_AND_DOWN = 7; // 0x7 field public static final int GESTURE_SWIPE_UP_AND_LEFT = 13; // 0xd field public static final int GESTURE_SWIPE_UP_AND_RIGHT = 14; // 0xe + field public static final int GESTURE_UNKNOWN = 0; // 0x0 field public static final int GLOBAL_ACTION_ACCESSIBILITY_ALL_APPS = 14; // 0xe field public static final int GLOBAL_ACTION_ACCESSIBILITY_BUTTON = 11; // 0xb field public static final int GLOBAL_ACTION_ACCESSIBILITY_BUTTON_CHOOSER = 12; // 0xc @@ -3043,6 +3049,7 @@ package android.accessibilityservice { field public static final int FLAG_REQUEST_SHORTCUT_WARNING_DIALOG_SPOKEN_FEEDBACK = 1024; // 0x400 field public static final int FLAG_REQUEST_TOUCH_EXPLORATION_MODE = 4; // 0x4 field public static final int FLAG_RETRIEVE_INTERACTIVE_WINDOWS = 64; // 0x40 + field public static final int FLAG_SEND_MOTION_EVENTS = 16384; // 0x4000 field public static final int FLAG_SERVICE_HANDLES_DOUBLE_TAP = 2048; // 0x800 field public int eventTypes; field public int feedbackType; @@ -4898,7 +4905,7 @@ package android.app { @Deprecated public class Fragment implements android.content.ComponentCallbacks2 android.view.View.OnCreateContextMenuListener { ctor @Deprecated public Fragment(); method @Deprecated public void dump(String, java.io.FileDescriptor, java.io.PrintWriter, String[]); - method @Deprecated public final boolean equals(Object); + method @Deprecated public final boolean equals(@Nullable Object); method @Deprecated public final android.app.Activity getActivity(); method @Deprecated public boolean getAllowEnterTransitionOverlap(); method @Deprecated public boolean getAllowReturnTransitionOverlap(); @@ -10664,12 +10671,15 @@ package android.content { field public static final String ACTION_PACKAGE_CHANGED = "android.intent.action.PACKAGE_CHANGED"; field public static final String ACTION_PACKAGE_DATA_CLEARED = "android.intent.action.PACKAGE_DATA_CLEARED"; field public static final String ACTION_PACKAGE_FIRST_LAUNCH = "android.intent.action.PACKAGE_FIRST_LAUNCH"; + field public static final String ACTION_PACKAGE_FULLY_LOADED = "android.intent.action.PACKAGE_FULLY_LOADED"; field public static final String ACTION_PACKAGE_FULLY_REMOVED = "android.intent.action.PACKAGE_FULLY_REMOVED"; field @Deprecated public static final String ACTION_PACKAGE_INSTALL = "android.intent.action.PACKAGE_INSTALL"; field public static final String ACTION_PACKAGE_NEEDS_VERIFICATION = "android.intent.action.PACKAGE_NEEDS_VERIFICATION"; field public static final String ACTION_PACKAGE_REMOVED = "android.intent.action.PACKAGE_REMOVED"; field public static final String ACTION_PACKAGE_REPLACED = "android.intent.action.PACKAGE_REPLACED"; field public static final String ACTION_PACKAGE_RESTARTED = "android.intent.action.PACKAGE_RESTARTED"; + field public static final String ACTION_PACKAGE_STARTABLE = "android.intent.action.PACKAGE_STARTABLE"; + field public static final String ACTION_PACKAGE_UNSTARTABLE = "android.intent.action.PACKAGE_UNSTARTABLE"; field public static final String ACTION_PACKAGE_VERIFIED = "android.intent.action.PACKAGE_VERIFIED"; field public static final String ACTION_PASTE = "android.intent.action.PASTE"; field public static final String ACTION_PICK = "android.intent.action.PICK"; @@ -10834,6 +10844,7 @@ package android.content { field public static final String EXTRA_TIMEZONE = "time-zone"; field public static final String EXTRA_TITLE = "android.intent.extra.TITLE"; field public static final String EXTRA_UID = "android.intent.extra.UID"; + field public static final String EXTRA_UNSTARTABLE_REASON = "android.intent.extra.UNSTARTABLE_REASON"; field public static final String EXTRA_USER = "android.intent.extra.USER"; field public static final int FILL_IN_ACTION = 1; // 0x1 field public static final int FILL_IN_CATEGORIES = 4; // 0x4 @@ -12293,6 +12304,9 @@ package android.content.pm { field public static final int SYNCHRONOUS = 2; // 0x2 field @Nullable public static final java.util.List<java.security.cert.Certificate> TRUST_ALL; field @NonNull public static final java.util.List<java.security.cert.Certificate> TRUST_NONE; + field public static final int UNSTARTABLE_REASON_CONNECTION_ERROR = 1; // 0x1 + field public static final int UNSTARTABLE_REASON_INSUFFICIENT_STORAGE = 2; // 0x2 + field public static final int UNSTARTABLE_REASON_UNKNOWN = 0; // 0x0 field public static final int VERIFICATION_ALLOW = 1; // 0x1 field public static final int VERIFICATION_REJECT = -1; // 0xffffffff field public static final int VERSION_CODE_HIGHEST = -1; // 0xffffffff @@ -13063,38 +13077,38 @@ package android.database { public interface Cursor extends java.io.Closeable { method public void close(); - method public void copyStringToBuffer(int, android.database.CharArrayBuffer); + method public void copyStringToBuffer(@IntRange(from=0) int, android.database.CharArrayBuffer); method @Deprecated public void deactivate(); - method public byte[] getBlob(int); - method public int getColumnCount(); - method public int getColumnIndex(String); - method public int getColumnIndexOrThrow(String) throws java.lang.IllegalArgumentException; - method public String getColumnName(int); + method public byte[] getBlob(@IntRange(from=0) int); + method @IntRange(from=0) public int getColumnCount(); + method @IntRange(from=0xffffffff) public int getColumnIndex(String); + method @IntRange(from=0) public int getColumnIndexOrThrow(String) throws java.lang.IllegalArgumentException; + method public String getColumnName(@IntRange(from=0) int); method public String[] getColumnNames(); - method public int getCount(); - method public double getDouble(int); + method @IntRange(from=0) public int getCount(); + method public double getDouble(@IntRange(from=0) int); method public android.os.Bundle getExtras(); - method public float getFloat(int); - method public int getInt(int); - method public long getLong(int); + method public float getFloat(@IntRange(from=0) int); + method public int getInt(@IntRange(from=0) int); + method public long getLong(@IntRange(from=0) int); method public android.net.Uri getNotificationUri(); method @Nullable public default java.util.List<android.net.Uri> getNotificationUris(); - method public int getPosition(); - method public short getShort(int); - method public String getString(int); - method public int getType(int); + method @IntRange(from=0xffffffff) public int getPosition(); + method public short getShort(@IntRange(from=0) int); + method public String getString(@IntRange(from=0) int); + method public int getType(@IntRange(from=0) int); method public boolean getWantsAllOnMoveCalls(); method public boolean isAfterLast(); method public boolean isBeforeFirst(); method public boolean isClosed(); method public boolean isFirst(); method public boolean isLast(); - method public boolean isNull(int); + method public boolean isNull(@IntRange(from=0) int); method public boolean move(int); method public boolean moveToFirst(); method public boolean moveToLast(); method public boolean moveToNext(); - method public boolean moveToPosition(int); + method public boolean moveToPosition(@IntRange(from=0xffffffff) int); method public boolean moveToPrevious(); method public void registerContentObserver(android.database.ContentObserver); method public void registerDataSetObserver(android.database.DataSetObserver); @@ -13136,33 +13150,33 @@ package android.database { ctor @Deprecated public CursorWindow(boolean); method public boolean allocRow(); method public void clear(); - method public void copyStringToBuffer(int, int, android.database.CharArrayBuffer); + method public void copyStringToBuffer(@IntRange(from=0) int, @IntRange(from=0) int, android.database.CharArrayBuffer); method public int describeContents(); method public void freeLastRow(); - method public byte[] getBlob(int, int); - method public double getDouble(int, int); - method public float getFloat(int, int); - method public int getInt(int, int); - method public long getLong(int, int); - method public int getNumRows(); - method public short getShort(int, int); - method public int getStartPosition(); - method public String getString(int, int); - method public int getType(int, int); - method @Deprecated public boolean isBlob(int, int); - method @Deprecated public boolean isFloat(int, int); - method @Deprecated public boolean isLong(int, int); - method @Deprecated public boolean isNull(int, int); - method @Deprecated public boolean isString(int, int); + method public byte[] getBlob(@IntRange(from=0) int, @IntRange(from=0) int); + method public double getDouble(@IntRange(from=0) int, @IntRange(from=0) int); + method public float getFloat(@IntRange(from=0) int, @IntRange(from=0) int); + method public int getInt(@IntRange(from=0) int, @IntRange(from=0) int); + method public long getLong(@IntRange(from=0) int, @IntRange(from=0) int); + method @IntRange(from=0) public int getNumRows(); + method public short getShort(@IntRange(from=0) int, @IntRange(from=0) int); + method @IntRange(from=0) public int getStartPosition(); + method public String getString(@IntRange(from=0) int, @IntRange(from=0) int); + method public int getType(@IntRange(from=0) int, @IntRange(from=0) int); + method @Deprecated public boolean isBlob(@IntRange(from=0) int, @IntRange(from=0) int); + method @Deprecated public boolean isFloat(@IntRange(from=0) int, @IntRange(from=0) int); + method @Deprecated public boolean isLong(@IntRange(from=0) int, @IntRange(from=0) int); + method @Deprecated public boolean isNull(@IntRange(from=0) int, @IntRange(from=0) int); + method @Deprecated public boolean isString(@IntRange(from=0) int, @IntRange(from=0) int); method public static android.database.CursorWindow newFromParcel(android.os.Parcel); method protected void onAllReferencesReleased(); - method public boolean putBlob(byte[], int, int); - method public boolean putDouble(double, int, int); - method public boolean putLong(long, int, int); - method public boolean putNull(int, int); - method public boolean putString(String, int, int); - method public boolean setNumColumns(int); - method public void setStartPosition(int); + method public boolean putBlob(byte[], @IntRange(from=0) int, @IntRange(from=0) int); + method public boolean putDouble(double, @IntRange(from=0) int, @IntRange(from=0) int); + method public boolean putLong(long, @IntRange(from=0) int, @IntRange(from=0) int); + method public boolean putNull(@IntRange(from=0) int, @IntRange(from=0) int); + method public boolean putString(String, @IntRange(from=0) int, @IntRange(from=0) int); + method public boolean setNumColumns(@IntRange(from=0) int); + method public void setStartPosition(@IntRange(from=0) int); method public void writeToParcel(android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.database.CursorWindow> CREATOR; } @@ -16504,23 +16518,6 @@ package android.graphics.pdf { package android.graphics.text { - public class GlyphStyle { - ctor public GlyphStyle(@ColorInt int, @FloatRange(from=0) float, @FloatRange(from=0) float, @FloatRange(from=0) float, int); - ctor public GlyphStyle(@NonNull android.graphics.Paint); - method public void applyToPaint(@NonNull android.graphics.Paint); - method @ColorInt public int getColor(); - method public int getFlags(); - method @FloatRange(from=0) public float getFontSize(); - method @FloatRange(from=0) public float getScaleX(); - method @FloatRange(from=0) public float getSkewX(); - method public void setColor(@ColorInt int); - method public void setFlags(int); - method public void setFontSize(@FloatRange(from=0) float); - method public void setFromPaint(@NonNull android.graphics.Paint); - method public void setScaleX(@FloatRange(from=0) float); - method public void setSkewX(@FloatRange(from=0) float); - } - public class LineBreaker { method @NonNull public android.graphics.text.LineBreaker.Result computeLineBreaks(@NonNull android.graphics.text.MeasuredText, @NonNull android.graphics.text.LineBreaker.ParagraphConstraints, @IntRange(from=0) int); field public static final int BREAK_STRATEGY_BALANCED = 2; // 0x2 @@ -16582,20 +16579,19 @@ package android.graphics.text { } public final class PositionedGlyphs { + method public float getAdvance(); method public float getAscent(); method public float getDescent(); method @NonNull public android.graphics.fonts.Font getFont(@IntRange(from=0) int); method @IntRange(from=0) public int getGlyphId(@IntRange(from=0) int); - method public float getOriginX(); - method public float getOriginY(); - method public float getPositionX(@IntRange(from=0) int); - method public float getPositionY(@IntRange(from=0) int); - method @NonNull public android.graphics.text.GlyphStyle getStyle(); - method public float getTotalAdvance(); + method public float getGlyphX(@IntRange(from=0) int); + method public float getGlyphY(@IntRange(from=0) int); + method public float getOffsetX(); + method public float getOffsetY(); method @IntRange(from=0) public int glyphCount(); } - public class TextShaper { + public class TextRunShaper { method @NonNull public static android.graphics.text.PositionedGlyphs shapeTextRun(@NonNull char[], int, int, int, int, float, float, boolean, @NonNull android.graphics.Paint); method @NonNull public static android.graphics.text.PositionedGlyphs shapeTextRun(@NonNull CharSequence, int, int, int, int, float, float, boolean, @NonNull android.graphics.Paint); } @@ -24071,9 +24067,13 @@ package android.location { method @IntRange(from=1, to=java.lang.Integer.MAX_VALUE) public int getMaxUpdates(); method @FloatRange(from=0, to=java.lang.Float.MAX_VALUE) public float getMinUpdateDistanceMeters(); method @IntRange(from=0) public long getMinUpdateIntervalMillis(); + method public int getQuality(); method public void writeToParcel(@NonNull android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.location.LocationRequest> CREATOR; field public static final long PASSIVE_INTERVAL = 9223372036854775807L; // 0x7fffffffffffffffL + field public static final int QUALITY_BALANCED_POWER_ACCURACY = 102; // 0x66 + field public static final int QUALITY_HIGH_ACCURACY = 100; // 0x64 + field public static final int QUALITY_LOW_POWER = 104; // 0x68 } public static final class LocationRequest.Builder { @@ -24086,6 +24086,7 @@ package android.location { method @NonNull public android.location.LocationRequest.Builder setMaxUpdates(@IntRange(from=1, to=java.lang.Integer.MAX_VALUE) int); method @NonNull public android.location.LocationRequest.Builder setMinUpdateDistanceMeters(@FloatRange(from=0, to=java.lang.Float.MAX_VALUE) float); method @NonNull public android.location.LocationRequest.Builder setMinUpdateIntervalMillis(@IntRange(from=0) long); + method @NonNull public android.location.LocationRequest.Builder setQuality(int); } public interface OnNmeaMessageListener { @@ -30015,9 +30016,12 @@ package android.net { method public int describeContents(); method @NonNull public byte[] getKey(); method @NonNull public String getName(); + method @NonNull public static java.util.Set<java.lang.String> getSupportedAlgorithms(); method public int getTruncationLengthBits(); method public void writeToParcel(android.os.Parcel, int); + field public static final String AUTH_AES_XCBC = "xcbc(aes)"; field public static final String AUTH_CRYPT_AES_GCM = "rfc4106(gcm(aes))"; + field public static final String AUTH_CRYPT_CHACHA20_POLY1305 = "rfc7539esp(chacha20,poly1305)"; field public static final String AUTH_HMAC_MD5 = "hmac(md5)"; field public static final String AUTH_HMAC_SHA1 = "hmac(sha1)"; field public static final String AUTH_HMAC_SHA256 = "hmac(sha256)"; @@ -30025,6 +30029,7 @@ package android.net { field public static final String AUTH_HMAC_SHA512 = "hmac(sha512)"; field @NonNull public static final android.os.Parcelable.Creator<android.net.IpSecAlgorithm> CREATOR; field public static final String CRYPT_AES_CBC = "cbc(aes)"; + field public static final String CRYPT_AES_CTR = "rfc3686(ctr(aes))"; } public final class IpSecManager { @@ -41430,8 +41435,16 @@ package android.service.autofill { method @NonNull public java.util.Map<android.view.autofill.AutofillId,android.service.autofill.FieldClassification> getFieldsClassification(); method @NonNull public java.util.Set<java.lang.String> getIgnoredDatasetIds(); method @NonNull public java.util.Map<android.view.autofill.AutofillId,java.util.Set<java.lang.String>> getManuallyEnteredField(); + method public int getNoSaveReason(); method @NonNull public java.util.Set<java.lang.String> getSelectedDatasetIds(); method public int getType(); + field public static final int NO_SAVE_REASON_DATASET_MATCH = 6; // 0x6 + field public static final int NO_SAVE_REASON_FIELD_VALIDATION_FAILED = 5; // 0x5 + field public static final int NO_SAVE_REASON_HAS_EMPTY_REQUIRED = 3; // 0x3 + field public static final int NO_SAVE_REASON_NONE = 0; // 0x0 + field public static final int NO_SAVE_REASON_NO_SAVE_INFO = 1; // 0x1 + field public static final int NO_SAVE_REASON_NO_VALUE_CHANGED = 4; // 0x4 + field public static final int NO_SAVE_REASON_WITH_DELAY_SAVE_FLAG = 2; // 0x2 field public static final int TYPE_AUTHENTICATION_SELECTED = 2; // 0x2 field public static final int TYPE_CONTEXT_COMMITTED = 4; // 0x4 field public static final int TYPE_DATASETS_SHOWN = 5; // 0x5 @@ -48190,10 +48203,6 @@ package android.text { method @NonNull public android.text.StaticLayout.Builder setUseLineSpacingFromFallbacks(boolean); } - public class StyledTextShaper { - method @NonNull public static java.util.List<android.graphics.text.PositionedGlyphs> shapeText(@NonNull CharSequence, int, int, @NonNull android.text.TextDirectionHeuristic, @NonNull android.text.TextPaint); - } - public interface TextDirectionHeuristic { method public boolean isRtl(char[], int, int); method public boolean isRtl(CharSequence, int, int); @@ -48223,6 +48232,14 @@ package android.text { field @Px public float underlineThickness; } + public class TextShaper { + method public static void shapeText(@NonNull CharSequence, @IntRange(from=0) int, @IntRange(from=0) int, @NonNull android.text.TextDirectionHeuristic, @NonNull android.text.TextPaint, @NonNull android.text.TextShaper.GlyphsConsumer); + } + + public static interface TextShaper.GlyphsConsumer { + method public void accept(@IntRange(from=0) int, @IntRange(from=0) int, @NonNull android.graphics.text.PositionedGlyphs, @NonNull android.text.TextPaint); + } + public class TextUtils { method @Deprecated public static CharSequence commaEllipsize(CharSequence, android.text.TextPaint, float, String, String); method public static CharSequence concat(java.lang.CharSequence...); @@ -51852,11 +51869,12 @@ package android.view { method @Nullable public android.net.Uri getLinkUri(); method public int getSource(); field public static final int FLAG_CONVERT_TO_PLAIN_TEXT = 1; // 0x1 - field public static final int SOURCE_AUTOFILL = 3; // 0x3 - field public static final int SOURCE_CLIPBOARD = 0; // 0x0 - field public static final int SOURCE_DRAG_AND_DROP = 2; // 0x2 - field public static final int SOURCE_INPUT_METHOD = 1; // 0x1 - field public static final int SOURCE_PROCESS_TEXT = 4; // 0x4 + field public static final int SOURCE_APP = 0; // 0x0 + field public static final int SOURCE_AUTOFILL = 4; // 0x4 + field public static final int SOURCE_CLIPBOARD = 1; // 0x1 + field public static final int SOURCE_DRAG_AND_DROP = 3; // 0x3 + field public static final int SOURCE_INPUT_METHOD = 2; // 0x2 + field public static final int SOURCE_PROCESS_TEXT = 5; // 0x5 } public static final class OnReceiveContentCallback.Payload.Builder { diff --git a/non-updatable-api/module-lib-current.txt b/non-updatable-api/module-lib-current.txt index b19ce4886589..ae6ecc763aef 100644 --- a/non-updatable-api/module-lib-current.txt +++ b/non-updatable-api/module-lib-current.txt @@ -1,10 +1,20 @@ // Signature format: 2.0 package android.app { + public class ActivityManager { + method @RequiresPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER) public void addHomeVisibilityListener(@NonNull java.util.concurrent.Executor, @NonNull android.app.HomeVisibilityListener); + method @RequiresPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER) public void removeHomeVisibilityListener(@NonNull android.app.HomeVisibilityListener); + } + public class AppOpsManager { field public static final String OPSTR_NO_ISOLATED_STORAGE = "android:no_isolated_storage"; } + public abstract class HomeVisibilityListener { + ctor public HomeVisibilityListener(); + method public abstract void onHomeVisibilityChanged(boolean); + } + public class NotificationManager { method public boolean hasEnabledNotificationListener(@NonNull String, @NonNull android.os.UserHandle); field public static final String ACTION_NOTIFICATION_LISTENER_ENABLED_CHANGED = "android.app.action.NOTIFICATION_LISTENER_ENABLED_CHANGED"; @@ -83,6 +93,10 @@ package android.media.session { field public static final int RESULT_MEDIA_KEY_NOT_HANDLED = 0; // 0x0 } + public final class PlaybackState implements android.os.Parcelable { + method public boolean isActiveState(); + } + } package android.os { @@ -93,8 +107,6 @@ package android.os { public interface Parcelable { method public default int getStability(); - field public static final int PARCELABLE_STABILITY_LOCAL = 0; // 0x0 - field public static final int PARCELABLE_STABILITY_VINTF = 1; // 0x1 } public class StatsServiceManager { @@ -123,6 +135,18 @@ package android.provider { } +package android.telephony { + + public abstract class CellSignalStrength { + method public static int getNumSignalStrengthLevels(); + } + + public class TelephonyManager { + method @NonNull public static int[] getAllNetworkTypes(); + } + +} + package android.util { public class AtomicFile { diff --git a/non-updatable-api/system-current.txt b/non-updatable-api/system-current.txt index 75e038be1a2b..26c307141065 100644 --- a/non-updatable-api/system-current.txt +++ b/non-updatable-api/system-current.txt @@ -122,6 +122,7 @@ package android { field public static final String MANAGE_FACTORY_RESET_PROTECTION = "android.permission.MANAGE_FACTORY_RESET_PROTECTION"; field public static final String MANAGE_IPSEC_TUNNELS = "android.permission.MANAGE_IPSEC_TUNNELS"; field public static final String MANAGE_MUSIC_RECOGNITION = "android.permission.MANAGE_MUSIC_RECOGNITION"; + field public static final String MANAGE_NOTIFICATION_LISTENERS = "android.permission.MANAGE_NOTIFICATION_LISTENERS"; field public static final String MANAGE_ONE_TIME_PERMISSION_SESSIONS = "android.permission.MANAGE_ONE_TIME_PERMISSION_SESSIONS"; field public static final String MANAGE_ROLE_HOLDERS = "android.permission.MANAGE_ROLE_HOLDERS"; field public static final String MANAGE_ROLLBACKS = "android.permission.MANAGE_ROLLBACKS"; @@ -224,6 +225,7 @@ package android { field public static final String SET_WALLPAPER_COMPONENT = "android.permission.SET_WALLPAPER_COMPONENT"; field public static final String SHOW_KEYGUARD_MESSAGE = "android.permission.SHOW_KEYGUARD_MESSAGE"; field public static final String SHUTDOWN = "android.permission.SHUTDOWN"; + field public static final String START_ACTIVITIES_FROM_BACKGROUND = "android.permission.START_ACTIVITIES_FROM_BACKGROUND"; field public static final String STATUS_BAR_SERVICE = "android.permission.STATUS_BAR_SERVICE"; field public static final String STOP_APP_SWITCHES = "android.permission.STOP_APP_SWITCHES"; field public static final String SUBSTITUTE_NOTIFICATION_APP_NAME = "android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME"; @@ -593,7 +595,7 @@ package android.app { public class BroadcastOptions { method public static android.app.BroadcastOptions makeBasic(); - method @RequiresPermission("android.permission.START_ACTIVITIES_FROM_BACKGROUND") public void setBackgroundActivityStartsAllowed(boolean); + method @RequiresPermission(android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND) public void setBackgroundActivityStartsAllowed(boolean); method public void setDontSendToRestrictedApps(boolean); method @RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST) public void setTemporaryAppWhitelistDuration(long); method public android.os.Bundle toBundle(); @@ -674,8 +676,10 @@ package android.app { public class NotificationManager { method @NonNull public java.util.List<java.lang.String> getAllowedAssistantAdjustments(); method @Nullable public android.content.ComponentName getAllowedNotificationAssistant(); + method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_NOTIFICATION_LISTENERS) public java.util.List<android.content.ComponentName> getEnabledNotificationListeners(); method public boolean isNotificationAssistantAccessGranted(@NonNull android.content.ComponentName); method public void setNotificationAssistantAccessGranted(@Nullable android.content.ComponentName, boolean); + method @RequiresPermission(android.Manifest.permission.MANAGE_NOTIFICATION_LISTENERS) public void setNotificationListenerAccessGranted(@NonNull android.content.ComponentName, boolean); field @RequiresPermission(android.Manifest.permission.STATUS_BAR_SERVICE) public static final String ACTION_CLOSE_NOTIFICATION_HANDLER_PANEL = "android.app.action.CLOSE_NOTIFICATION_HANDLER_PANEL"; field @RequiresPermission(android.Manifest.permission.STATUS_BAR_SERVICE) public static final String ACTION_OPEN_NOTIFICATION_HANDLER_PANEL = "android.app.action.OPEN_NOTIFICATION_HANDLER_PANEL"; field @RequiresPermission(android.Manifest.permission.STATUS_BAR_SERVICE) public static final String ACTION_TOGGLE_NOTIFICATION_HANDLER_PANEL = "android.app.action.TOGGLE_NOTIFICATION_HANDLER_PANEL"; @@ -1388,20 +1392,20 @@ package android.app.time { package android.app.usage { public final class CacheQuotaHint implements android.os.Parcelable { - ctor public CacheQuotaHint(android.app.usage.CacheQuotaHint.Builder); + ctor public CacheQuotaHint(@NonNull android.app.usage.CacheQuotaHint.Builder); method public int describeContents(); method public long getQuota(); method public int getUid(); - method public android.app.usage.UsageStats getUsageStats(); - method public String getVolumeUuid(); - method public void writeToParcel(android.os.Parcel, int); + method @Nullable public android.app.usage.UsageStats getUsageStats(); + method @Nullable public String getVolumeUuid(); + method public void writeToParcel(@NonNull android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.app.usage.CacheQuotaHint> CREATOR; field public static final long QUOTA_NOT_SET = -1L; // 0xffffffffffffffffL } public static final class CacheQuotaHint.Builder { ctor public CacheQuotaHint.Builder(); - ctor public CacheQuotaHint.Builder(android.app.usage.CacheQuotaHint); + ctor public CacheQuotaHint.Builder(@NonNull android.app.usage.CacheQuotaHint); method @NonNull public android.app.usage.CacheQuotaHint build(); method @NonNull public android.app.usage.CacheQuotaHint.Builder setQuota(long); method @NonNull public android.app.usage.CacheQuotaHint.Builder setUid(int); @@ -1739,6 +1743,7 @@ package android.content { field public static final String APP_PREDICTION_SERVICE = "app_prediction"; field public static final String BACKUP_SERVICE = "backup"; field public static final String BATTERY_STATS_SERVICE = "batterystats"; + field public static final int BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS = 1048576; // 0x100000 field public static final String BUGREPORT_SERVICE = "bugreport"; field public static final String CONTENT_SUGGESTIONS_SERVICE = "content_suggestions"; field public static final String CONTEXTHUB_SERVICE = "contexthub"; @@ -4111,7 +4116,6 @@ package android.location { method @Deprecated public long getInterval(); method @Deprecated public int getNumUpdates(); method @Deprecated @NonNull public String getProvider(); - method public int getQuality(); method @Deprecated public float getSmallestDisplacement(); method @NonNull public android.os.WorkSource getWorkSource(); method public boolean isHiddenFromAppOps(); @@ -4130,11 +4134,11 @@ package android.location { method @Deprecated @NonNull public android.location.LocationRequest setQuality(int); method @Deprecated @NonNull public android.location.LocationRequest setSmallestDisplacement(float); method @Deprecated public void setWorkSource(@Nullable android.os.WorkSource); - field public static final int ACCURACY_BLOCK = 102; // 0x66 - field public static final int ACCURACY_CITY = 104; // 0x68 - field public static final int ACCURACY_FINE = 100; // 0x64 - field public static final int POWER_HIGH = 203; // 0xcb - field public static final int POWER_LOW = 201; // 0xc9 + field @Deprecated public static final int ACCURACY_BLOCK = 102; // 0x66 + field @Deprecated public static final int ACCURACY_CITY = 104; // 0x68 + field @Deprecated public static final int ACCURACY_FINE = 100; // 0x64 + field @Deprecated public static final int POWER_HIGH = 203; // 0xcb + field @Deprecated public static final int POWER_LOW = 201; // 0xc9 field @Deprecated public static final int POWER_NONE = 200; // 0xc8 } @@ -4142,7 +4146,6 @@ package android.location { method @NonNull @RequiresPermission(android.Manifest.permission.UPDATE_APP_OPS_STATS) public android.location.LocationRequest.Builder setHiddenFromAppOps(boolean); method @NonNull @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public android.location.LocationRequest.Builder setLocationSettingsIgnored(boolean); method @NonNull @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public android.location.LocationRequest.Builder setLowPower(boolean); - method @NonNull public android.location.LocationRequest.Builder setQuality(int); method @NonNull @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public android.location.LocationRequest.Builder setWorkSource(@Nullable android.os.WorkSource); } @@ -4709,6 +4712,22 @@ package android.media.tv { field @NonNull public static final android.os.Parcelable.Creator<android.media.tv.DvbDeviceInfo> CREATOR; } + public final class TunedInfo implements android.os.Parcelable { + method public int describeContents(); + method public int getAppTag(); + method public int getAppType(); + method @Nullable public android.net.Uri getChannelUri(); + method @NonNull public String getInputId(); + method public boolean isForeground(); + method public boolean isRecordingSession(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field public static final int APP_TAG_SELF = 0; // 0x0 + field public static final int APP_TYPE_NON_SYSTEM = 3; // 0x3 + field public static final int APP_TYPE_SELF = 1; // 0x1 + field public static final int APP_TYPE_SYSTEM = 2; // 0x2 + field @NonNull public static final android.os.Parcelable.Creator<android.media.tv.TunedInfo> CREATOR; + } + public final class TvContentRatingSystemInfo implements android.os.Parcelable { method public static android.media.tv.TvContentRatingSystemInfo createTvContentRatingSystemInfo(int, android.content.pm.ApplicationInfo); method public int describeContents(); @@ -4824,6 +4843,7 @@ package android.media.tv { method @RequiresPermission(android.Manifest.permission.MODIFY_PARENTAL_CONTROLS) public void addBlockedRating(@NonNull android.media.tv.TvContentRating); method @RequiresPermission(android.Manifest.permission.CAPTURE_TV_INPUT) public boolean captureFrame(String, android.view.Surface, android.media.tv.TvStreamConfig); method @RequiresPermission(android.Manifest.permission.CAPTURE_TV_INPUT) public java.util.List<android.media.tv.TvStreamConfig> getAvailableTvStreamConfigList(String); + method @NonNull @RequiresPermission("com.android.providers.tv.permission.ACCESS_WATCHED_PROGRAMS") public java.util.List<android.media.tv.TunedInfo> getCurrentTunedInfos(); method @NonNull @RequiresPermission("android.permission.DVB_DEVICE") public java.util.List<android.media.tv.DvbDeviceInfo> getDvbDeviceList(); method @RequiresPermission(android.Manifest.permission.TV_INPUT_HARDWARE) public java.util.List<android.media.tv.TvInputHardwareInfo> getHardwareList(); method @RequiresPermission(android.Manifest.permission.READ_CONTENT_RATING_SYSTEMS) public java.util.List<android.media.tv.TvContentRatingSystemInfo> getTvContentRatingSystemList(); @@ -4849,6 +4869,10 @@ package android.media.tv { method public abstract void onStreamConfigChanged(android.media.tv.TvStreamConfig[]); } + public abstract static class TvInputManager.TvInputCallback { + method public void onCurrentTunedInfosUpdated(@NonNull java.util.List<android.media.tv.TunedInfo>); + } + public abstract class TvInputService extends android.app.Service { method @Nullable public android.media.tv.TvInputInfo onHardwareAdded(android.media.tv.TvInputHardwareInfo); method @Nullable public String onHardwareRemoved(android.media.tv.TvInputHardwareInfo); @@ -4976,6 +5000,7 @@ package android.media.tv.tuner { method @Nullable public android.media.tv.tuner.DemuxCapabilities getDemuxCapabilities(); method @Nullable public android.media.tv.tuner.frontend.FrontendInfo getFrontendInfo(); method @Nullable public android.media.tv.tuner.frontend.FrontendStatus getFrontendStatus(@NonNull int[]); + method public int linkFrontendToCiCam(int); method @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_TV_DESCRAMBLER) public android.media.tv.tuner.Descrambler openDescrambler(); method @Nullable public android.media.tv.tuner.dvr.DvrPlayback openDvrPlayback(long, @NonNull java.util.concurrent.Executor, @NonNull android.media.tv.tuner.dvr.OnPlaybackStatusChangedListener); method @Nullable public android.media.tv.tuner.dvr.DvrRecorder openDvrRecorder(long, @NonNull java.util.concurrent.Executor, @NonNull android.media.tv.tuner.dvr.OnRecordStatusChangedListener); @@ -4989,10 +5014,13 @@ package android.media.tv.tuner { method public void setResourceLostListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.tv.tuner.Tuner.OnResourceLostListener); method public void shareFrontendFromTuner(@NonNull android.media.tv.tuner.Tuner); method public int tune(@NonNull android.media.tv.tuner.frontend.FrontendSettings); + method public int unlinkFrontendToCiCam(int); method public void updateResourcePriority(int, int); field public static final int INVALID_AV_SYNC_ID = -1; // 0xffffffff field public static final int INVALID_FILTER_ID = -1; // 0xffffffff field public static final long INVALID_FILTER_ID_64BIT = -1L; // 0xffffffffffffffffL + field public static final int INVALID_FRONTEND_SETTING_FREQUENCY = -1; // 0xffffffff + field public static final int INVALID_LTS_ID = -1; // 0xffffffff field public static final int INVALID_MMTP_RECORD_EVENT_MPT_SEQUENCE_NUM = -1; // 0xffffffff field public static final int INVALID_STREAM_ID = 65535; // 0xffff field public static final long INVALID_TIMESTAMP = -1L; // 0xffffffffffffffffL @@ -5025,10 +5053,10 @@ package android.media.tv.tuner { package android.media.tv.tuner.dvr { public class DvrPlayback implements java.lang.AutoCloseable { - method public int attachFilter(@NonNull android.media.tv.tuner.filter.Filter); + method @Deprecated public int attachFilter(@NonNull android.media.tv.tuner.filter.Filter); method public void close(); method public int configure(@NonNull android.media.tv.tuner.dvr.DvrSettings); - method public int detachFilter(@NonNull android.media.tv.tuner.filter.Filter); + method @Deprecated public int detachFilter(@NonNull android.media.tv.tuner.filter.Filter); method public int flush(); method public long read(long); method public long read(@NonNull byte[], long, long); @@ -5122,12 +5150,45 @@ package android.media.tv.tuner.filter { public class AvSettings extends android.media.tv.tuner.filter.Settings { method @NonNull public static android.media.tv.tuner.filter.AvSettings.Builder builder(int, boolean); + method public int getAudioStreamType(); + method public int getVideoStreamType(); method public boolean isPassthrough(); + field public static final int AUDIO_STREAM_TYPE_AAC = 6; // 0x6 + field public static final int AUDIO_STREAM_TYPE_AC3 = 7; // 0x7 + field public static final int AUDIO_STREAM_TYPE_AC4 = 9; // 0x9 + field public static final int AUDIO_STREAM_TYPE_DRA = 15; // 0xf + field public static final int AUDIO_STREAM_TYPE_DTS = 10; // 0xa + field public static final int AUDIO_STREAM_TYPE_DTS_HD = 11; // 0xb + field public static final int AUDIO_STREAM_TYPE_EAC3 = 8; // 0x8 + field public static final int AUDIO_STREAM_TYPE_MP3 = 2; // 0x2 + field public static final int AUDIO_STREAM_TYPE_MPEG1 = 3; // 0x3 + field public static final int AUDIO_STREAM_TYPE_MPEG2 = 4; // 0x4 + field public static final int AUDIO_STREAM_TYPE_MPEGH = 5; // 0x5 + field public static final int AUDIO_STREAM_TYPE_OPUS = 13; // 0xd + field public static final int AUDIO_STREAM_TYPE_PCM = 1; // 0x1 + field public static final int AUDIO_STREAM_TYPE_UNDEFINED = 0; // 0x0 + field public static final int AUDIO_STREAM_TYPE_VORBIS = 14; // 0xe + field public static final int AUDIO_STREAM_TYPE_WMA = 12; // 0xc + field public static final int VIDEO_STREAM_TYPE_AV1 = 10; // 0xa + field public static final int VIDEO_STREAM_TYPE_AVC = 5; // 0x5 + field public static final int VIDEO_STREAM_TYPE_AVS = 11; // 0xb + field public static final int VIDEO_STREAM_TYPE_AVS2 = 12; // 0xc + field public static final int VIDEO_STREAM_TYPE_HEVC = 6; // 0x6 + field public static final int VIDEO_STREAM_TYPE_MPEG1 = 2; // 0x2 + field public static final int VIDEO_STREAM_TYPE_MPEG2 = 3; // 0x3 + field public static final int VIDEO_STREAM_TYPE_MPEG4P2 = 4; // 0x4 + field public static final int VIDEO_STREAM_TYPE_RESERVED = 1; // 0x1 + field public static final int VIDEO_STREAM_TYPE_UNDEFINED = 0; // 0x0 + field public static final int VIDEO_STREAM_TYPE_VC1 = 7; // 0x7 + field public static final int VIDEO_STREAM_TYPE_VP8 = 8; // 0x8 + field public static final int VIDEO_STREAM_TYPE_VP9 = 9; // 0x9 } public static class AvSettings.Builder { method @NonNull public android.media.tv.tuner.filter.AvSettings build(); + method @NonNull public android.media.tv.tuner.filter.AvSettings.Builder setAudioStreamType(int); method @NonNull public android.media.tv.tuner.filter.AvSettings.Builder setPassthrough(boolean); + method @NonNull public android.media.tv.tuner.filter.AvSettings.Builder setVideoStreamType(int); } public class DownloadEvent extends android.media.tv.tuner.filter.FilterEvent { @@ -5205,16 +5266,19 @@ package android.media.tv.tuner.filter { method @NonNull public static android.media.tv.tuner.filter.IpFilterConfiguration.Builder builder(); method @NonNull @Size(min=4, max=16) public byte[] getDstIpAddress(); method public int getDstPort(); + method @IntRange(from=0, to=61439) public int getIpFilterContextId(); method @NonNull @Size(min=4, max=16) public byte[] getSrcIpAddress(); method public int getSrcPort(); method public int getType(); method public boolean isPassthrough(); + field public static final int INVALID_IP_FILTER_CONTEXT_ID = -1; // 0xffffffff } public static final class IpFilterConfiguration.Builder { method @NonNull public android.media.tv.tuner.filter.IpFilterConfiguration build(); method @NonNull public android.media.tv.tuner.filter.IpFilterConfiguration.Builder setDstIpAddress(@NonNull byte[]); method @NonNull public android.media.tv.tuner.filter.IpFilterConfiguration.Builder setDstPort(int); + method @NonNull public android.media.tv.tuner.filter.IpFilterConfiguration.Builder setIpFilterContextId(int); method @NonNull public android.media.tv.tuner.filter.IpFilterConfiguration.Builder setPassthrough(boolean); method @NonNull public android.media.tv.tuner.filter.IpFilterConfiguration.Builder setSettings(@Nullable android.media.tv.tuner.filter.Settings); method @NonNull public android.media.tv.tuner.filter.IpFilterConfiguration.Builder setSrcIpAddress(@NonNull byte[]); @@ -5238,6 +5302,7 @@ package android.media.tv.tuner.filter { method public boolean isPrivateData(); method public boolean isPtsPresent(); method public boolean isSecureMemory(); + method public void release(); } public final class MmtpFilterConfiguration extends android.media.tv.tuner.filter.FilterConfiguration { @@ -5434,9 +5499,13 @@ package android.media.tv.tuner.frontend { public class AnalogFrontendSettings extends android.media.tv.tuner.frontend.FrontendSettings { method @NonNull public static android.media.tv.tuner.frontend.AnalogFrontendSettings.Builder builder(); + method public int getAftFlag(); method public int getSifStandard(); method public int getSignalType(); method public int getType(); + field public static final int AFT_FLAG_FALSE = 2; // 0x2 + field public static final int AFT_FLAG_TRUE = 1; // 0x1 + field public static final int AFT_FLAG_UNDEFINED = 0; // 0x0 field public static final int SIF_AUTO = 1; // 0x1 field public static final int SIF_BG = 2; // 0x2 field public static final int SIF_BG_A2 = 4; // 0x4 @@ -5469,6 +5538,7 @@ package android.media.tv.tuner.frontend { public static class AnalogFrontendSettings.Builder { method @NonNull public android.media.tv.tuner.frontend.AnalogFrontendSettings build(); + method @NonNull public android.media.tv.tuner.frontend.AnalogFrontendSettings.Builder setAftFlag(int); method @IntRange(from=1) @NonNull public android.media.tv.tuner.frontend.AnalogFrontendSettings.Builder setFrequency(int); method @NonNull public android.media.tv.tuner.frontend.AnalogFrontendSettings.Builder setSifStandard(int); method @NonNull public android.media.tv.tuner.frontend.AnalogFrontendSettings.Builder setSignalType(int); @@ -5584,6 +5654,69 @@ package android.media.tv.tuner.frontend { method @NonNull public android.media.tv.tuner.frontend.AtscFrontendSettings.Builder setModulation(int); } + public class DtmbFrontendCapabilities extends android.media.tv.tuner.frontend.FrontendCapabilities { + method public int getBandwidthCapability(); + method public int getCodeRateCapability(); + method public int getGuardIntervalCapability(); + method public int getModulationCapability(); + method public int getTimeInterleaveModeCapability(); + method public int getTransmissionModeCapability(); + } + + public class DtmbFrontendSettings extends android.media.tv.tuner.frontend.FrontendSettings { + method @NonNull public static android.media.tv.tuner.frontend.DtmbFrontendSettings.Builder builder(); + method public int getBandwidth(); + method public int getCodeRate(); + method public int getGuardInterval(); + method public int getModulation(); + method public int getTimeInterleaveMode(); + method public int getTransmissionMode(); + method public int getType(); + field public static final int BANDWIDTH_6MHZ = 4; // 0x4 + field public static final int BANDWIDTH_8MHZ = 2; // 0x2 + field public static final int BANDWIDTH_AUTO = 1; // 0x1 + field public static final int BANDWIDTH_UNDEFINED = 0; // 0x0 + field public static final int CODERATE_2_5 = 2; // 0x2 + field public static final int CODERATE_3_5 = 4; // 0x4 + field public static final int CODERATE_4_5 = 8; // 0x8 + field public static final int CODERATE_AUTO = 1; // 0x1 + field public static final int CODERATE_UNDEFINED = 0; // 0x0 + field public static final int GUARD_INTERVAL_AUTO = 1; // 0x1 + field public static final int GUARD_INTERVAL_PN_420_CONST = 16; // 0x10 + field public static final int GUARD_INTERVAL_PN_420_VARIOUS = 2; // 0x2 + field public static final int GUARD_INTERVAL_PN_595_CONST = 4; // 0x4 + field public static final int GUARD_INTERVAL_PN_945_CONST = 32; // 0x20 + field public static final int GUARD_INTERVAL_PN_945_VARIOUS = 8; // 0x8 + field public static final int GUARD_INTERVAL_PN_RESERVED = 64; // 0x40 + field public static final int GUARD_INTERVAL_UNDEFINED = 0; // 0x0 + field public static final int MODULATION_CONSTELLATION_16QAM = 8; // 0x8 + field public static final int MODULATION_CONSTELLATION_32QAM = 16; // 0x10 + field public static final int MODULATION_CONSTELLATION_4QAM = 2; // 0x2 + field public static final int MODULATION_CONSTELLATION_4QAM_NR = 4; // 0x4 + field public static final int MODULATION_CONSTELLATION_64QAM = 32; // 0x20 + field public static final int MODULATION_CONSTELLATION_AUTO = 1; // 0x1 + field public static final int MODULATION_CONSTELLATION_UNDEFINED = 0; // 0x0 + field public static final int TIME_INTERLEAVE_MODE_AUTO = 1; // 0x1 + field public static final int TIME_INTERLEAVE_MODE_TIMER_INT_240 = 2; // 0x2 + field public static final int TIME_INTERLEAVE_MODE_TIMER_INT_720 = 4; // 0x4 + field public static final int TIME_INTERLEAVE_MODE_UNDEFINED = 0; // 0x0 + field public static final int TRANSMISSION_MODE_AUTO = 1; // 0x1 + field public static final int TRANSMISSION_MODE_C1 = 2; // 0x2 + field public static final int TRANSMISSION_MODE_C3780 = 4; // 0x4 + field public static final int TRANSMISSION_MODE_UNDEFINED = 0; // 0x0 + } + + public static final class DtmbFrontendSettings.Builder { + method @NonNull public android.media.tv.tuner.frontend.DtmbFrontendSettings build(); + method @NonNull public android.media.tv.tuner.frontend.DtmbFrontendSettings.Builder setBandwidth(int); + method @NonNull public android.media.tv.tuner.frontend.DtmbFrontendSettings.Builder setCodeRate(int); + method @IntRange(from=1) @NonNull public android.media.tv.tuner.frontend.DtmbFrontendSettings.Builder setFrequency(int); + method @NonNull public android.media.tv.tuner.frontend.DtmbFrontendSettings.Builder setGuardInterval(int); + method @NonNull public android.media.tv.tuner.frontend.DtmbFrontendSettings.Builder setModulation(int); + method @NonNull public android.media.tv.tuner.frontend.DtmbFrontendSettings.Builder setTimeInterleaveMode(int); + method @NonNull public android.media.tv.tuner.frontend.DtmbFrontendSettings.Builder setTransmissionMode(int); + } + public class DvbcFrontendCapabilities extends android.media.tv.tuner.frontend.FrontendCapabilities { method public int getAnnexCapability(); method public int getFecCapability(); @@ -5598,6 +5731,7 @@ package android.media.tv.tuner.frontend { method public int getOuterFec(); method public int getSpectralInversion(); method public int getSymbolRate(); + method public int getTimeInterleaveMode(); method public int getType(); field public static final int ANNEX_A = 1; // 0x1 field public static final int ANNEX_B = 2; // 0x2 @@ -5613,9 +5747,20 @@ package android.media.tv.tuner.frontend { field public static final int OUTER_FEC_OUTER_FEC_NONE = 1; // 0x1 field public static final int OUTER_FEC_OUTER_FEC_RS = 2; // 0x2 field public static final int OUTER_FEC_UNDEFINED = 0; // 0x0 - field public static final int SPECTRAL_INVERSION_INVERTED = 2; // 0x2 - field public static final int SPECTRAL_INVERSION_NORMAL = 1; // 0x1 - field public static final int SPECTRAL_INVERSION_UNDEFINED = 0; // 0x0 + field @Deprecated public static final int SPECTRAL_INVERSION_INVERTED = 2; // 0x2 + field @Deprecated public static final int SPECTRAL_INVERSION_NORMAL = 1; // 0x1 + field @Deprecated public static final int SPECTRAL_INVERSION_UNDEFINED = 0; // 0x0 + field public static final int TIME_INTERLEAVE_MODE_128_1_0 = 2; // 0x2 + field public static final int TIME_INTERLEAVE_MODE_128_1_1 = 4; // 0x4 + field public static final int TIME_INTERLEAVE_MODE_128_2 = 128; // 0x80 + field public static final int TIME_INTERLEAVE_MODE_128_3 = 256; // 0x100 + field public static final int TIME_INTERLEAVE_MODE_128_4 = 512; // 0x200 + field public static final int TIME_INTERLEAVE_MODE_16_8 = 32; // 0x20 + field public static final int TIME_INTERLEAVE_MODE_32_4 = 16; // 0x10 + field public static final int TIME_INTERLEAVE_MODE_64_2 = 8; // 0x8 + field public static final int TIME_INTERLEAVE_MODE_8_16 = 64; // 0x40 + field public static final int TIME_INTERLEAVE_MODE_AUTO = 1; // 0x1 + field public static final int TIME_INTERLEAVE_MODE_UNDEFINED = 0; // 0x0 } public static class DvbcFrontendSettings.Builder { @@ -5627,6 +5772,7 @@ package android.media.tv.tuner.frontend { method @NonNull public android.media.tv.tuner.frontend.DvbcFrontendSettings.Builder setOuterFec(int); method @NonNull public android.media.tv.tuner.frontend.DvbcFrontendSettings.Builder setSpectralInversion(int); method @NonNull public android.media.tv.tuner.frontend.DvbcFrontendSettings.Builder setSymbolRate(int); + method @NonNull public android.media.tv.tuner.frontend.DvbcFrontendSettings.Builder setTimeInterleaveMode(int); } public class DvbsCodeRate { @@ -5658,10 +5804,12 @@ package android.media.tv.tuner.frontend { method public int getModulation(); method public int getPilot(); method public int getRolloff(); + method public int getScanType(); method public int getStandard(); method public int getSymbolRate(); method public int getType(); method public int getVcmMode(); + method public boolean isDiseqcRxMessage(); field public static final int MODULATION_AUTO = 1; // 0x1 field public static final int MODULATION_MOD_128APSK = 2048; // 0x800 field public static final int MODULATION_MOD_16APSK = 256; // 0x100 @@ -5688,6 +5836,11 @@ package android.media.tv.tuner.frontend { field public static final int ROLLOFF_0_35 = 1; // 0x1 field public static final int ROLLOFF_0_5 = 6; // 0x6 field public static final int ROLLOFF_UNDEFINED = 0; // 0x0 + field public static final int SCAN_TYPE_DIRECT = 1; // 0x1 + field public static final int SCAN_TYPE_DISEQC = 2; // 0x2 + field public static final int SCAN_TYPE_JESS = 4; // 0x4 + field public static final int SCAN_TYPE_UNDEFINED = 0; // 0x0 + field public static final int SCAN_TYPE_UNICABLE = 3; // 0x3 field public static final int STANDARD_AUTO = 1; // 0x1 field public static final int STANDARD_S = 2; // 0x2 field public static final int STANDARD_S2 = 4; // 0x4 @@ -5700,11 +5853,13 @@ package android.media.tv.tuner.frontend { public static class DvbsFrontendSettings.Builder { method @NonNull public android.media.tv.tuner.frontend.DvbsFrontendSettings build(); method @NonNull public android.media.tv.tuner.frontend.DvbsFrontendSettings.Builder setCodeRate(@Nullable android.media.tv.tuner.frontend.DvbsCodeRate); + method @NonNull public android.media.tv.tuner.frontend.DvbsFrontendSettings.Builder setDiseqcRxMessage(boolean); method @IntRange(from=1) @NonNull public android.media.tv.tuner.frontend.DvbsFrontendSettings.Builder setFrequency(int); method @NonNull public android.media.tv.tuner.frontend.DvbsFrontendSettings.Builder setInputStreamId(int); method @NonNull public android.media.tv.tuner.frontend.DvbsFrontendSettings.Builder setModulation(int); method @NonNull public android.media.tv.tuner.frontend.DvbsFrontendSettings.Builder setPilot(int); method @NonNull public android.media.tv.tuner.frontend.DvbsFrontendSettings.Builder setRolloff(int); + method @NonNull public android.media.tv.tuner.frontend.DvbsFrontendSettings.Builder setScanType(int); method @NonNull public android.media.tv.tuner.frontend.DvbsFrontendSettings.Builder setStandard(int); method @NonNull public android.media.tv.tuner.frontend.DvbsFrontendSettings.Builder setSymbolRate(int); method @NonNull public android.media.tv.tuner.frontend.DvbsFrontendSettings.Builder setVcmMode(int); @@ -5757,10 +5912,14 @@ package android.media.tv.tuner.frontend { field public static final int CODERATE_AUTO = 1; // 0x1 field public static final int CODERATE_UNDEFINED = 0; // 0x0 field public static final int CONSTELLATION_16QAM = 4; // 0x4 + field public static final int CONSTELLATION_16QAM_R = 64; // 0x40 field public static final int CONSTELLATION_256QAM = 16; // 0x10 + field public static final int CONSTELLATION_256QAM_R = 256; // 0x100 field public static final int CONSTELLATION_64QAM = 8; // 0x8 + field public static final int CONSTELLATION_64QAM_R = 128; // 0x80 field public static final int CONSTELLATION_AUTO = 1; // 0x1 field public static final int CONSTELLATION_QPSK = 2; // 0x2 + field public static final int CONSTELLATION_QPSK_R = 32; // 0x20 field public static final int CONSTELLATION_UNDEFINED = 0; // 0x0 field public static final int GUARD_INTERVAL_19_128 = 64; // 0x40 field public static final int GUARD_INTERVAL_19_256 = 128; // 0x80 @@ -5794,6 +5953,9 @@ package android.media.tv.tuner.frontend { field public static final int TRANSMISSION_MODE_4K = 8; // 0x8 field public static final int TRANSMISSION_MODE_8K = 4; // 0x4 field public static final int TRANSMISSION_MODE_AUTO = 1; // 0x1 + field public static final int TRANSMISSION_MODE_EXTENDED_16K = 256; // 0x100 + field public static final int TRANSMISSION_MODE_EXTENDED_32K = 512; // 0x200 + field public static final int TRANSMISSION_MODE_EXTENDED_8K = 128; // 0x80 field public static final int TRANSMISSION_MODE_UNDEFINED = 0; // 0x0 } @@ -5831,8 +5993,12 @@ package android.media.tv.tuner.frontend { } public abstract class FrontendSettings { + method public int getEndFrequency(); method public int getFrequency(); + method public int getFrontendSpectralInversion(); method public abstract int getType(); + method @IntRange(from=1) public void setEndFrequency(int); + method public void setSpectralInversion(int); field public static final long FEC_11_15 = 4194304L; // 0x400000L field public static final long FEC_11_20 = 8388608L; // 0x800000L field public static final long FEC_11_45 = 16777216L; // 0x1000000L @@ -5870,9 +6036,13 @@ package android.media.tv.tuner.frontend { field public static final long FEC_9_20 = 2097152L; // 0x200000L field public static final long FEC_AUTO = 1L; // 0x1L field public static final long FEC_UNDEFINED = 0L; // 0x0L + field public static final int FRONTEND_SPECTRAL_INVERSION_INVERTED = 2; // 0x2 + field public static final int FRONTEND_SPECTRAL_INVERSION_NORMAL = 1; // 0x1 + field public static final int FRONTEND_SPECTRAL_INVERSION_UNDEFINED = 0; // 0x0 field public static final int TYPE_ANALOG = 1; // 0x1 field public static final int TYPE_ATSC = 2; // 0x2 field public static final int TYPE_ATSC3 = 3; // 0x3 + field public static final int TYPE_DTMB = 10; // 0xa field public static final int TYPE_DVBC = 4; // 0x4 field public static final int TYPE_DVBS = 5; // 0x5 field public static final int TYPE_DVBT = 6; // 0x6 @@ -5885,14 +6055,21 @@ package android.media.tv.tuner.frontend { public class FrontendStatus { method public int getAgc(); method @NonNull public android.media.tv.tuner.frontend.FrontendStatus.Atsc3PlpTuningInfo[] getAtsc3PlpTuningInfo(); + method public int getBandwidth(); method public int getBer(); + method @NonNull public int[] getBers(); + method @NonNull public int[] getCodeRates(); method public int getFreqOffset(); + method public int getGuardInterval(); method public int getHierarchy(); method public long getInnerFec(); + method @NonNull public int[] getInterleaving(); + method @NonNull public int[] getIsdbtSegment(); method @NonNull public boolean[] getLayerErrors(); method public int getLnbVoltage(); method public int getMer(); method public int getModulation(); + method @NonNull public int[] getModulationsExt(); method public int getPer(); method public int getPerBer(); method public int getPlpId(); @@ -5901,23 +6078,34 @@ package android.media.tv.tuner.frontend { method public int getSnr(); method public int getSpectralInversion(); method public int getSymbolRate(); + method public int getSystemId(); + method public int getTransmissionMode(); + method @NonNull public int[] getTsDataRate(); + method public int getUec(); method public boolean isDemodLocked(); method public boolean isEwbs(); method public boolean isLnaOn(); method public boolean isRfLocked(); field public static final int FRONTEND_STATUS_TYPE_AGC = 14; // 0xe field public static final int FRONTEND_STATUS_TYPE_ATSC3_PLP_INFO = 21; // 0x15 + field public static final int FRONTEND_STATUS_TYPE_BANDWIDTH = 25; // 0x19 field public static final int FRONTEND_STATUS_TYPE_BER = 2; // 0x2 + field public static final int FRONTEND_STATUS_TYPE_BERS = 23; // 0x17 + field public static final int FRONTEND_STATUS_TYPE_CODERATES = 24; // 0x18 field public static final int FRONTEND_STATUS_TYPE_DEMOD_LOCK = 0; // 0x0 field public static final int FRONTEND_STATUS_TYPE_EWBS = 13; // 0xd field public static final int FRONTEND_STATUS_TYPE_FEC = 8; // 0x8 field public static final int FRONTEND_STATUS_TYPE_FREQ_OFFSET = 18; // 0x12 + field public static final int FRONTEND_STATUS_TYPE_GUARD_INTERVAL = 26; // 0x1a field public static final int FRONTEND_STATUS_TYPE_HIERARCHY = 19; // 0x13 + field public static final int FRONTEND_STATUS_TYPE_INTERLEAVINGS = 30; // 0x1e + field public static final int FRONTEND_STATUS_TYPE_ISDBT_SEGMENTS = 31; // 0x1f field public static final int FRONTEND_STATUS_TYPE_LAYER_ERROR = 16; // 0x10 field public static final int FRONTEND_STATUS_TYPE_LNA = 15; // 0xf field public static final int FRONTEND_STATUS_TYPE_LNB_VOLTAGE = 11; // 0xb field public static final int FRONTEND_STATUS_TYPE_MER = 17; // 0x11 field public static final int FRONTEND_STATUS_TYPE_MODULATION = 9; // 0x9 + field public static final int FRONTEND_STATUS_TYPE_MODULATIONS_EXT = 22; // 0x16 field public static final int FRONTEND_STATUS_TYPE_PER = 3; // 0x3 field public static final int FRONTEND_STATUS_TYPE_PLP_ID = 12; // 0xc field public static final int FRONTEND_STATUS_TYPE_PRE_BER = 4; // 0x4 @@ -5927,6 +6115,10 @@ package android.media.tv.tuner.frontend { field public static final int FRONTEND_STATUS_TYPE_SNR = 1; // 0x1 field public static final int FRONTEND_STATUS_TYPE_SPECTRAL = 10; // 0xa field public static final int FRONTEND_STATUS_TYPE_SYMBOL_RATE = 7; // 0x7 + field public static final int FRONTEND_STATUS_TYPE_T2_SYSTEM_ID = 29; // 0x1d + field public static final int FRONTEND_STATUS_TYPE_TRANSMISSION_MODE = 27; // 0x1b + field public static final int FRONTEND_STATUS_TYPE_TS_DATA_RATES = 32; // 0x20 + field public static final int FRONTEND_STATUS_TYPE_UEC = 28; // 0x1c } public static class FrontendStatus.Atsc3PlpTuningInfo { @@ -6090,7 +6282,9 @@ package android.media.tv.tuner.frontend { method public void onHierarchyReported(int); method public void onInputStreamIdsReported(@NonNull int[]); method public void onLocked(); + method public default void onModulationReported(int); method public void onPlpIdsReported(@NonNull int[]); + method public default void onPriorityReported(boolean); method public void onProgress(@IntRange(from=0, to=100) int); method public void onScanStopped(); method public void onSignalTypeReported(int); @@ -7509,6 +7703,22 @@ package android.os { method public boolean hasSingleFileDescriptor(); } + public interface Parcelable { + field public static final int PARCELABLE_STABILITY_LOCAL = 0; // 0x0 + field public static final int PARCELABLE_STABILITY_VINTF = 1; // 0x1 + } + + public final class ParcelableHolder implements android.os.Parcelable { + ctor public ParcelableHolder(int); + method public int describeContents(); + method @Nullable public <T extends android.os.Parcelable> T getParcelable(@NonNull Class<T>); + method public int getStability(); + method public void readFromParcel(@NonNull android.os.Parcel); + method public boolean setParcelable(@Nullable android.os.Parcelable); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.os.ParcelableHolder> CREATOR; + } + public final class PowerManager { method @RequiresPermission(allOf={android.Manifest.permission.READ_DREAM_STATE, android.Manifest.permission.WRITE_DREAM_STATE}) public void dream(long); method @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public boolean forceSuspend(); @@ -9899,6 +10109,25 @@ package android.telephony { field public static final String MBMS_STREAMING_SERVICE_ACTION = "android.telephony.action.EmbmsStreaming"; } + public final class ModemActivityInfo implements android.os.Parcelable { + method public int describeContents(); + method @NonNull public android.telephony.ModemActivityInfo getDelta(@NonNull android.telephony.ModemActivityInfo); + method public long getIdleTimeMillis(); + method public static int getNumTxPowerLevels(); + method public long getReceiveTimeMillis(); + method public long getSleepTimeMillis(); + method public long getTimestampMillis(); + method public long getTransmitDurationMillisAtPowerLevel(int); + method @NonNull public android.util.Range<java.lang.Integer> getTransmitPowerRange(int); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ModemActivityInfo> CREATOR; + field public static final int TX_POWER_LEVEL_0 = 0; // 0x0 + field public static final int TX_POWER_LEVEL_1 = 1; // 0x1 + field public static final int TX_POWER_LEVEL_2 = 2; // 0x2 + field public static final int TX_POWER_LEVEL_3 = 3; // 0x3 + field public static final int TX_POWER_LEVEL_4 = 4; // 0x4 + } + public final class NetworkRegistrationInfo implements android.os.Parcelable { method @Nullable public android.telephony.DataSpecificRegistrationInfo getDataSpecificInfo(); method public int getRegistrationState(); @@ -10151,6 +10380,10 @@ package android.telephony { field public static final int ROAMING_TYPE_UNKNOWN = 1; // 0x1 } + public class SignalStrength implements android.os.Parcelable { + ctor public SignalStrength(@NonNull android.telephony.SignalStrength); + } + public final class SmsCbCmasInfo implements android.os.Parcelable { ctor public SmsCbCmasInfo(int, int, int, int, int, int); method public int describeContents(); @@ -11926,7 +12159,7 @@ package android.webkit { method @Nullable public String findProxyForUrl(@NonNull String); method @NonNull public static android.webkit.PacProcessor getInstance(); method @Nullable public default android.net.Network getNetwork(); - method public default void releasePacProcessor(); + method public default void release(); method public default void setNetwork(@Nullable android.net.Network); method public boolean setProxyScript(@NonNull String); } diff --git a/packages/CarSystemUI/AndroidManifest.xml b/packages/CarSystemUI/AndroidManifest.xml index c0482e1b3e04..f0cf2a9b7156 100644 --- a/packages/CarSystemUI/AndroidManifest.xml +++ b/packages/CarSystemUI/AndroidManifest.xml @@ -23,6 +23,8 @@ <uses-permission android:name="android.car.permission.CAR_POWER" /> <!-- This permission is required to get the trusted device list of a user. --> <uses-permission android:name="android.car.permission.CAR_ENROLL_TRUST"/> + <!-- This permission is required to monitor gear state. --> + <uses-permission android:name="android.car.permission.CAR_POWERTRAIN" /> <!-- This permission is required to get bluetooth broadcast. --> <uses-permission android:name="android.permission.BLUETOOTH" /> <!-- These permissions are required to implement icons based on role holders. --> diff --git a/packages/CarSystemUI/res/layout/rear_view_camera.xml b/packages/CarSystemUI/res/layout/rear_view_camera.xml new file mode 100644 index 000000000000..9b9898cf3f45 --- /dev/null +++ b/packages/CarSystemUI/res/layout/rear_view_camera.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2020 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<LinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/rear_view_camera_container" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:background="@android:color/transparent" + android:orientation="vertical"> + <Button + android:id="@+id/close_button" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:background="@color/rear_view_camera_button_background" + android:text="@string/rear_view_camera_close_button_text" + android:textAppearance="?android:attr/textAppearanceLarge"/> +</LinearLayout> diff --git a/packages/CarSystemUI/res/layout/sysui_overlay_window.xml b/packages/CarSystemUI/res/layout/sysui_overlay_window.xml index 2c9788955bfa..980265e84e54 100644 --- a/packages/CarSystemUI/res/layout/sysui_overlay_window.xml +++ b/packages/CarSystemUI/res/layout/sysui_overlay_window.xml @@ -42,4 +42,10 @@ android:layout_height="match_parent" android:layout="@layout/car_user_switching_dialog"/> -</FrameLayout>
\ No newline at end of file + <!-- Should be at bottom to get the highest Z-order. --> + <ViewStub android:id="@+id/rear_view_camera_stub" + android:layout_width="@dimen/rear_view_camera_width" + android:layout_height="@dimen/rear_view_camera_height" + android:layout_gravity="center" + android:layout="@layout/rear_view_camera"/> +</FrameLayout> diff --git a/packages/CarSystemUI/res/values-af/strings.xml b/packages/CarSystemUI/res/values-af/strings.xml index 2ab3aa44a86c..b377ec47f6c6 100644 --- a/packages/CarSystemUI/res/values-af/strings.xml +++ b/packages/CarSystemUI/res/values-af/strings.xml @@ -28,4 +28,6 @@ <string name="user_add_user_message_update" msgid="7061671307004867811">"Enige gebruiker kan programme vir al die ander gebruikers opdateer."</string> <string name="car_loading_profile" msgid="4507385037552574474">"Laai tans"</string> <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Laai tans gebruiker (van <xliff:g id="FROM_USER">%1$d</xliff:g> na <xliff:g id="TO_USER">%2$d</xliff:g>)"</string> + <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) --> + <skip /> </resources> diff --git a/packages/CarSystemUI/res/values-am/strings.xml b/packages/CarSystemUI/res/values-am/strings.xml index d5b580e3dd9d..4f2bba8aa1de 100644 --- a/packages/CarSystemUI/res/values-am/strings.xml +++ b/packages/CarSystemUI/res/values-am/strings.xml @@ -28,4 +28,6 @@ <string name="user_add_user_message_update" msgid="7061671307004867811">"ማንኛውም ተጠቃሚ መተግበሪያዎችን ለሌሎች ተጠቃሚዎች ሁሉ ማዘመን ይችላል።"</string> <string name="car_loading_profile" msgid="4507385037552574474">"በመጫን ላይ"</string> <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"ተጠቃሚን (ከ<xliff:g id="FROM_USER">%1$d</xliff:g> ወደ <xliff:g id="TO_USER">%2$d</xliff:g>) በመጫን ላይ"</string> + <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) --> + <skip /> </resources> diff --git a/packages/CarSystemUI/res/values-ar/strings.xml b/packages/CarSystemUI/res/values-ar/strings.xml index 09b302d85eab..61a08a4eae49 100644 --- a/packages/CarSystemUI/res/values-ar/strings.xml +++ b/packages/CarSystemUI/res/values-ar/strings.xml @@ -28,4 +28,6 @@ <string name="user_add_user_message_update" msgid="7061671307004867811">"يمكن لأي مستخدم تحديث التطبيقات لجميع المستخدمين الآخرين."</string> <string name="car_loading_profile" msgid="4507385037552574474">"جارٍ التحميل"</string> <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"جارٍ تحميل الملف الشخصي الجديد للمستخدم (من <xliff:g id="FROM_USER">%1$d</xliff:g> إلى <xliff:g id="TO_USER">%2$d</xliff:g>)"</string> + <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) --> + <skip /> </resources> diff --git a/packages/CarSystemUI/res/values-as/strings.xml b/packages/CarSystemUI/res/values-as/strings.xml index a8aa74d8c7a4..edc36215bcce 100644 --- a/packages/CarSystemUI/res/values-as/strings.xml +++ b/packages/CarSystemUI/res/values-as/strings.xml @@ -28,4 +28,6 @@ <string name="user_add_user_message_update" msgid="7061671307004867811">"যিকোনো ব্যৱহাৰকাৰীয়ে অন্য ব্যৱহাৰকাৰীৰ বাবে এপ্সমূহ আপডে’ট কৰিব পাৰে।"</string> <string name="car_loading_profile" msgid="4507385037552574474">"ল’ড হৈ আছে"</string> <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"ব্যৱহাৰকাৰী ল’ড হৈ আছে (<xliff:g id="FROM_USER">%1$d</xliff:g>ৰ পৰা to <xliff:g id="TO_USER">%2$d</xliff:g>লৈ)"</string> + <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) --> + <skip /> </resources> diff --git a/packages/CarSystemUI/res/values-az/strings.xml b/packages/CarSystemUI/res/values-az/strings.xml index 97facc27be68..398f5c38c03e 100644 --- a/packages/CarSystemUI/res/values-az/strings.xml +++ b/packages/CarSystemUI/res/values-az/strings.xml @@ -28,4 +28,6 @@ <string name="user_add_user_message_update" msgid="7061671307004867811">"İstənilən istifadəçi digər bütün istifadəçilər üçün tətbiqləri güncəlləyə bilər."</string> <string name="car_loading_profile" msgid="4507385037552574474">"Yüklənir"</string> <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"İstifadəçi yüklənir (<xliff:g id="FROM_USER">%1$d</xliff:g>-<xliff:g id="TO_USER">%2$d</xliff:g>)"</string> + <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) --> + <skip /> </resources> diff --git a/packages/CarSystemUI/res/values-b+sr+Latn/strings.xml b/packages/CarSystemUI/res/values-b+sr+Latn/strings.xml index ec310be7a4d8..6c1979f7a95a 100644 --- a/packages/CarSystemUI/res/values-b+sr+Latn/strings.xml +++ b/packages/CarSystemUI/res/values-b+sr+Latn/strings.xml @@ -28,4 +28,6 @@ <string name="user_add_user_message_update" msgid="7061671307004867811">"Svaki korisnik može da ažurira aplikacije za sve ostale korisnike."</string> <string name="car_loading_profile" msgid="4507385037552574474">"Učitava se"</string> <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Profil korisnika se učitava (iz<xliff:g id="FROM_USER">%1$d</xliff:g> u <xliff:g id="TO_USER">%2$d</xliff:g>)"</string> + <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) --> + <skip /> </resources> diff --git a/packages/CarSystemUI/res/values-be/strings.xml b/packages/CarSystemUI/res/values-be/strings.xml index 17fc58451464..4e9794855e47 100644 --- a/packages/CarSystemUI/res/values-be/strings.xml +++ b/packages/CarSystemUI/res/values-be/strings.xml @@ -28,4 +28,6 @@ <string name="user_add_user_message_update" msgid="7061671307004867811">"Кожны карыстальнік прылады можа абнаўляць праграмы для ўсіх іншых карыстальнікаў."</string> <string name="car_loading_profile" msgid="4507385037552574474">"Ідзе загрузка"</string> <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Ідзе загрузка профілю карыстальніка (ад <xliff:g id="FROM_USER">%1$d</xliff:g> да <xliff:g id="TO_USER">%2$d</xliff:g>)"</string> + <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) --> + <skip /> </resources> diff --git a/packages/CarSystemUI/res/values-bg/strings.xml b/packages/CarSystemUI/res/values-bg/strings.xml index ae2db2d08f25..7dfab54b8add 100644 --- a/packages/CarSystemUI/res/values-bg/strings.xml +++ b/packages/CarSystemUI/res/values-bg/strings.xml @@ -28,4 +28,6 @@ <string name="user_add_user_message_update" msgid="7061671307004867811">"Всеки потребител може да актуализира приложенията за всички останали потребители."</string> <string name="car_loading_profile" msgid="4507385037552574474">"Зарежда се"</string> <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Потребителят се зарежда (от <xliff:g id="FROM_USER">%1$d</xliff:g> до <xliff:g id="TO_USER">%2$d</xliff:g>)"</string> + <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) --> + <skip /> </resources> diff --git a/packages/CarSystemUI/res/values-bn/strings.xml b/packages/CarSystemUI/res/values-bn/strings.xml index 9a01fad952eb..a45e66e8c2f2 100644 --- a/packages/CarSystemUI/res/values-bn/strings.xml +++ b/packages/CarSystemUI/res/values-bn/strings.xml @@ -28,4 +28,6 @@ <string name="user_add_user_message_update" msgid="7061671307004867811">"যেকোনও ব্যবহারকারী বাকি সব ব্যবহারকারীর জন্য অ্যাপ আপডেট করতে পারবেন।"</string> <string name="car_loading_profile" msgid="4507385037552574474">"লোড হচ্ছে"</string> <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"ব্যবহারকারীর প্রোফাইল লোড করা হচ্ছে (<xliff:g id="FROM_USER">%1$d</xliff:g> থেকে <xliff:g id="TO_USER">%2$d</xliff:g>-এ)"</string> + <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) --> + <skip /> </resources> diff --git a/packages/CarSystemUI/res/values-bs/strings.xml b/packages/CarSystemUI/res/values-bs/strings.xml index 5ef9aeba3436..119f2d7bf793 100644 --- a/packages/CarSystemUI/res/values-bs/strings.xml +++ b/packages/CarSystemUI/res/values-bs/strings.xml @@ -28,4 +28,6 @@ <string name="user_add_user_message_update" msgid="7061671307004867811">"Bilo koji korisnik može ažurirati aplikacije za sve druge korisnike."</string> <string name="car_loading_profile" msgid="4507385037552574474">"Učitavanje"</string> <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Učitavanje korisnika (od korisnika <xliff:g id="FROM_USER">%1$d</xliff:g> do korisnika <xliff:g id="TO_USER">%2$d</xliff:g>)"</string> + <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) --> + <skip /> </resources> diff --git a/packages/CarSystemUI/res/values-ca/strings.xml b/packages/CarSystemUI/res/values-ca/strings.xml index 083f9d05069e..b1e722e95c58 100644 --- a/packages/CarSystemUI/res/values-ca/strings.xml +++ b/packages/CarSystemUI/res/values-ca/strings.xml @@ -28,4 +28,6 @@ <string name="user_add_user_message_update" msgid="7061671307004867811">"Qualsevol usuari pot actualitzar les aplicacions de la resta d\'usuaris."</string> <string name="car_loading_profile" msgid="4507385037552574474">"S\'està carregant"</string> <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"S\'està carregant l\'usuari (de <xliff:g id="FROM_USER">%1$d</xliff:g> a <xliff:g id="TO_USER">%2$d</xliff:g>)"</string> + <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) --> + <skip /> </resources> diff --git a/packages/CarSystemUI/res/values-cs/strings.xml b/packages/CarSystemUI/res/values-cs/strings.xml index 8071cef72650..dd4472f06aa3 100644 --- a/packages/CarSystemUI/res/values-cs/strings.xml +++ b/packages/CarSystemUI/res/values-cs/strings.xml @@ -28,4 +28,6 @@ <string name="user_add_user_message_update" msgid="7061671307004867811">"Každý uživatel může aktualizovat aplikace všech ostatních uživatelů."</string> <string name="car_loading_profile" msgid="4507385037552574474">"Načítání"</string> <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Načítání uživatele (předchozí: <xliff:g id="FROM_USER">%1$d</xliff:g>, následující: <xliff:g id="TO_USER">%2$d</xliff:g>)"</string> + <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) --> + <skip /> </resources> diff --git a/packages/CarSystemUI/res/values-da/strings.xml b/packages/CarSystemUI/res/values-da/strings.xml index b19cdcba5af9..6c08aa56bc7e 100644 --- a/packages/CarSystemUI/res/values-da/strings.xml +++ b/packages/CarSystemUI/res/values-da/strings.xml @@ -28,4 +28,6 @@ <string name="user_add_user_message_update" msgid="7061671307004867811">"Alle brugere kan opdatere apps for alle andre brugere."</string> <string name="car_loading_profile" msgid="4507385037552574474">"Indlæser"</string> <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Indlæser bruger (fra <xliff:g id="FROM_USER">%1$d</xliff:g> til <xliff:g id="TO_USER">%2$d</xliff:g>)"</string> + <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) --> + <skip /> </resources> diff --git a/packages/CarSystemUI/res/values-de/strings.xml b/packages/CarSystemUI/res/values-de/strings.xml index 99ba13e4f3f8..131dee19b0ca 100644 --- a/packages/CarSystemUI/res/values-de/strings.xml +++ b/packages/CarSystemUI/res/values-de/strings.xml @@ -28,4 +28,6 @@ <string name="user_add_user_message_update" msgid="7061671307004867811">"Jeder Nutzer kann Apps für alle anderen Nutzer aktualisieren."</string> <string name="car_loading_profile" msgid="4507385037552574474">"Wird geladen"</string> <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Nutzer wird geladen (von <xliff:g id="FROM_USER">%1$d</xliff:g> bis <xliff:g id="TO_USER">%2$d</xliff:g>)"</string> + <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) --> + <skip /> </resources> diff --git a/packages/CarSystemUI/res/values-el/strings.xml b/packages/CarSystemUI/res/values-el/strings.xml index e2d2cec34479..66f8d18472c9 100644 --- a/packages/CarSystemUI/res/values-el/strings.xml +++ b/packages/CarSystemUI/res/values-el/strings.xml @@ -28,4 +28,6 @@ <string name="user_add_user_message_update" msgid="7061671307004867811">"Οποιοσδήποτε χρήστης μπορεί να ενημερώσει τις εφαρμογές για όλους τους άλλους χρήστες."</string> <string name="car_loading_profile" msgid="4507385037552574474">"Φόρτωση"</string> <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Φόρτωση χρήστη (από <xliff:g id="FROM_USER">%1$d</xliff:g> έως <xliff:g id="TO_USER">%2$d</xliff:g>)"</string> + <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) --> + <skip /> </resources> diff --git a/packages/CarSystemUI/res/values-en-rAU/strings.xml b/packages/CarSystemUI/res/values-en-rAU/strings.xml index b8bf9906916d..b3e358fbb6ef 100644 --- a/packages/CarSystemUI/res/values-en-rAU/strings.xml +++ b/packages/CarSystemUI/res/values-en-rAU/strings.xml @@ -28,4 +28,6 @@ <string name="user_add_user_message_update" msgid="7061671307004867811">"Any user can update apps for all other users."</string> <string name="car_loading_profile" msgid="4507385037552574474">"Loading"</string> <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Loading user (from <xliff:g id="FROM_USER">%1$d</xliff:g> to <xliff:g id="TO_USER">%2$d</xliff:g>)"</string> + <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) --> + <skip /> </resources> diff --git a/packages/CarSystemUI/res/values-en-rCA/strings.xml b/packages/CarSystemUI/res/values-en-rCA/strings.xml index b8bf9906916d..b3e358fbb6ef 100644 --- a/packages/CarSystemUI/res/values-en-rCA/strings.xml +++ b/packages/CarSystemUI/res/values-en-rCA/strings.xml @@ -28,4 +28,6 @@ <string name="user_add_user_message_update" msgid="7061671307004867811">"Any user can update apps for all other users."</string> <string name="car_loading_profile" msgid="4507385037552574474">"Loading"</string> <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Loading user (from <xliff:g id="FROM_USER">%1$d</xliff:g> to <xliff:g id="TO_USER">%2$d</xliff:g>)"</string> + <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) --> + <skip /> </resources> diff --git a/packages/CarSystemUI/res/values-en-rGB/strings.xml b/packages/CarSystemUI/res/values-en-rGB/strings.xml index b8bf9906916d..b3e358fbb6ef 100644 --- a/packages/CarSystemUI/res/values-en-rGB/strings.xml +++ b/packages/CarSystemUI/res/values-en-rGB/strings.xml @@ -28,4 +28,6 @@ <string name="user_add_user_message_update" msgid="7061671307004867811">"Any user can update apps for all other users."</string> <string name="car_loading_profile" msgid="4507385037552574474">"Loading"</string> <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Loading user (from <xliff:g id="FROM_USER">%1$d</xliff:g> to <xliff:g id="TO_USER">%2$d</xliff:g>)"</string> + <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) --> + <skip /> </resources> diff --git a/packages/CarSystemUI/res/values-en-rIN/strings.xml b/packages/CarSystemUI/res/values-en-rIN/strings.xml index b8bf9906916d..b3e358fbb6ef 100644 --- a/packages/CarSystemUI/res/values-en-rIN/strings.xml +++ b/packages/CarSystemUI/res/values-en-rIN/strings.xml @@ -28,4 +28,6 @@ <string name="user_add_user_message_update" msgid="7061671307004867811">"Any user can update apps for all other users."</string> <string name="car_loading_profile" msgid="4507385037552574474">"Loading"</string> <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Loading user (from <xliff:g id="FROM_USER">%1$d</xliff:g> to <xliff:g id="TO_USER">%2$d</xliff:g>)"</string> + <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) --> + <skip /> </resources> diff --git a/packages/CarSystemUI/res/values-en-rXC/strings.xml b/packages/CarSystemUI/res/values-en-rXC/strings.xml index 1ffa5ebde689..eaf6f51d3092 100644 --- a/packages/CarSystemUI/res/values-en-rXC/strings.xml +++ b/packages/CarSystemUI/res/values-en-rXC/strings.xml @@ -28,4 +28,6 @@ <string name="user_add_user_message_update" msgid="7061671307004867811">"Any user can update apps for all other users."</string> <string name="car_loading_profile" msgid="4507385037552574474">"Loading"</string> <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Loading user (from <xliff:g id="FROM_USER">%1$d</xliff:g> to <xliff:g id="TO_USER">%2$d</xliff:g>)"</string> + <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) --> + <skip /> </resources> diff --git a/packages/CarSystemUI/res/values-es-rUS/strings.xml b/packages/CarSystemUI/res/values-es-rUS/strings.xml index c1c21d17a3a7..6a5f8ce43318 100644 --- a/packages/CarSystemUI/res/values-es-rUS/strings.xml +++ b/packages/CarSystemUI/res/values-es-rUS/strings.xml @@ -28,4 +28,6 @@ <string name="user_add_user_message_update" msgid="7061671307004867811">"Cualquier usuario podrá actualizar las apps de otras personas."</string> <string name="car_loading_profile" msgid="4507385037552574474">"Cargando"</string> <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Cargando usuario (de <xliff:g id="FROM_USER">%1$d</xliff:g> a <xliff:g id="TO_USER">%2$d</xliff:g>)"</string> + <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) --> + <skip /> </resources> diff --git a/packages/CarSystemUI/res/values-es/strings.xml b/packages/CarSystemUI/res/values-es/strings.xml index fe546051e729..c43d7e54d559 100644 --- a/packages/CarSystemUI/res/values-es/strings.xml +++ b/packages/CarSystemUI/res/values-es/strings.xml @@ -28,4 +28,6 @@ <string name="user_add_user_message_update" msgid="7061671307004867811">"Cualquier usuario puede actualizar las aplicaciones del resto de los usuarios."</string> <string name="car_loading_profile" msgid="4507385037552574474">"Cargando"</string> <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Cargando usuario (de <xliff:g id="FROM_USER">%1$d</xliff:g> a <xliff:g id="TO_USER">%2$d</xliff:g>)"</string> + <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) --> + <skip /> </resources> diff --git a/packages/CarSystemUI/res/values-et/strings.xml b/packages/CarSystemUI/res/values-et/strings.xml index 2fa71a975973..ad82d5fc230b 100644 --- a/packages/CarSystemUI/res/values-et/strings.xml +++ b/packages/CarSystemUI/res/values-et/strings.xml @@ -28,4 +28,6 @@ <string name="user_add_user_message_update" msgid="7061671307004867811">"Iga kasutaja saab rakendusi värskendada kõigi teiste kasutajate jaoks."</string> <string name="car_loading_profile" msgid="4507385037552574474">"Laadimine"</string> <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Kasutaja laadimine (<xliff:g id="FROM_USER">%1$d</xliff:g> > <xliff:g id="TO_USER">%2$d</xliff:g>)"</string> + <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) --> + <skip /> </resources> diff --git a/packages/CarSystemUI/res/values-eu/strings.xml b/packages/CarSystemUI/res/values-eu/strings.xml index 36bcaae01443..5d2ca3548af2 100644 --- a/packages/CarSystemUI/res/values-eu/strings.xml +++ b/packages/CarSystemUI/res/values-eu/strings.xml @@ -28,4 +28,6 @@ <string name="user_add_user_message_update" msgid="7061671307004867811">"Edozein erabiltzailek egunera ditzake beste erabiltzaile guztien aplikazioak."</string> <string name="car_loading_profile" msgid="4507385037552574474">"Kargatzen"</string> <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Erabiltzailea kargatzen (<xliff:g id="FROM_USER">%1$d</xliff:g> izatetik<xliff:g id="TO_USER">%2$d</xliff:g> izatera igaroko da)"</string> + <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) --> + <skip /> </resources> diff --git a/packages/CarSystemUI/res/values-fa/strings.xml b/packages/CarSystemUI/res/values-fa/strings.xml index 3224899afb90..ef37b654bde9 100644 --- a/packages/CarSystemUI/res/values-fa/strings.xml +++ b/packages/CarSystemUI/res/values-fa/strings.xml @@ -28,4 +28,6 @@ <string name="user_add_user_message_update" msgid="7061671307004867811">"هر کاربری میتواند برنامهها را برای همه کاربران دیگر بهروزرسانی کند."</string> <string name="car_loading_profile" msgid="4507385037552574474">"درحال بارگیری"</string> <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"بارگیری کاربر (از <xliff:g id="FROM_USER">%1$d</xliff:g> تا <xliff:g id="TO_USER">%2$d</xliff:g>)"</string> + <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) --> + <skip /> </resources> diff --git a/packages/CarSystemUI/res/values-fi/strings.xml b/packages/CarSystemUI/res/values-fi/strings.xml index 60689695725c..10bb0c5b782c 100644 --- a/packages/CarSystemUI/res/values-fi/strings.xml +++ b/packages/CarSystemUI/res/values-fi/strings.xml @@ -28,4 +28,6 @@ <string name="user_add_user_message_update" msgid="7061671307004867811">"Kaikki käyttäjät voivat päivittää muiden käyttäjien sovelluksia."</string> <string name="car_loading_profile" msgid="4507385037552574474">"Ladataan"</string> <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Ladataan käyttäjäprofiilia (<xliff:g id="FROM_USER">%1$d</xliff:g>–<xliff:g id="TO_USER">%2$d</xliff:g>)"</string> + <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) --> + <skip /> </resources> diff --git a/packages/CarSystemUI/res/values-fr-rCA/strings.xml b/packages/CarSystemUI/res/values-fr-rCA/strings.xml index 18e68c07d23d..bebd3f441410 100644 --- a/packages/CarSystemUI/res/values-fr-rCA/strings.xml +++ b/packages/CarSystemUI/res/values-fr-rCA/strings.xml @@ -28,4 +28,6 @@ <string name="user_add_user_message_update" msgid="7061671307004867811">"Tout utilisateur peut mettre à jour les applications pour tous les autres utilisateurs."</string> <string name="car_loading_profile" msgid="4507385037552574474">"Chargement en cours…"</string> <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Chargement de l\'utilisateur (de <xliff:g id="FROM_USER">%1$d</xliff:g> vers <xliff:g id="TO_USER">%2$d</xliff:g>) en cours…"</string> + <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) --> + <skip /> </resources> diff --git a/packages/CarSystemUI/res/values-fr/strings.xml b/packages/CarSystemUI/res/values-fr/strings.xml index 954df67d4b74..3d498d2f9ca7 100644 --- a/packages/CarSystemUI/res/values-fr/strings.xml +++ b/packages/CarSystemUI/res/values-fr/strings.xml @@ -28,4 +28,6 @@ <string name="user_add_user_message_update" msgid="7061671307004867811">"N\'importe quel utilisateur peut mettre à jour les applications pour tous les autres utilisateurs."</string> <string name="car_loading_profile" msgid="4507385037552574474">"Chargement…"</string> <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Chargement de l\'utilisateur (de <xliff:g id="FROM_USER">%1$d</xliff:g> à <xliff:g id="TO_USER">%2$d</xliff:g>)"</string> + <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) --> + <skip /> </resources> diff --git a/packages/CarSystemUI/res/values-gl/strings.xml b/packages/CarSystemUI/res/values-gl/strings.xml index 95bfbd30c0b8..e4586cc17a73 100644 --- a/packages/CarSystemUI/res/values-gl/strings.xml +++ b/packages/CarSystemUI/res/values-gl/strings.xml @@ -28,4 +28,6 @@ <string name="user_add_user_message_update" msgid="7061671307004867811">"Calquera usuario pode actualizar as aplicacións para o resto dos usuarios."</string> <string name="car_loading_profile" msgid="4507385037552574474">"Cargando"</string> <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Cargando usuario (do <xliff:g id="FROM_USER">%1$d</xliff:g> ao <xliff:g id="TO_USER">%2$d</xliff:g>)"</string> + <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) --> + <skip /> </resources> diff --git a/packages/CarSystemUI/res/values-gu/strings.xml b/packages/CarSystemUI/res/values-gu/strings.xml index dec991ce1400..ba884e410187 100644 --- a/packages/CarSystemUI/res/values-gu/strings.xml +++ b/packages/CarSystemUI/res/values-gu/strings.xml @@ -28,4 +28,6 @@ <string name="user_add_user_message_update" msgid="7061671307004867811">"કોઈપણ વપરાશકર્તા અન્ય બધા વપરાશકર્તાઓ માટે ઍપને અપડેટ કરી શકે છે."</string> <string name="car_loading_profile" msgid="4507385037552574474">"લોડ કરી રહ્યાં છીએ"</string> <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"વપરાશકર્તાને લોડ કરી રહ્યાં છીએ (<xliff:g id="FROM_USER">%1$d</xliff:g>માંથી <xliff:g id="TO_USER">%2$d</xliff:g>માં)"</string> + <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) --> + <skip /> </resources> diff --git a/packages/CarSystemUI/res/values-hi/strings.xml b/packages/CarSystemUI/res/values-hi/strings.xml index 89b69608efbe..95454a53709f 100644 --- a/packages/CarSystemUI/res/values-hi/strings.xml +++ b/packages/CarSystemUI/res/values-hi/strings.xml @@ -28,4 +28,6 @@ <string name="user_add_user_message_update" msgid="7061671307004867811">"कोई भी उपयोगकर्ता, बाकी सभी उपयोगकर्ताओं के लिए ऐप्लिकेशन अपडेट कर सकता है."</string> <string name="car_loading_profile" msgid="4507385037552574474">"लोड हो रही है"</string> <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"उपयोगकर्ता को लोड किया जा रहा है (<xliff:g id="FROM_USER">%1$d</xliff:g> से <xliff:g id="TO_USER">%2$d</xliff:g> पर)"</string> + <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) --> + <skip /> </resources> diff --git a/packages/CarSystemUI/res/values-hr/strings.xml b/packages/CarSystemUI/res/values-hr/strings.xml index 6baec74ddbff..f3aaf63eac18 100644 --- a/packages/CarSystemUI/res/values-hr/strings.xml +++ b/packages/CarSystemUI/res/values-hr/strings.xml @@ -28,4 +28,6 @@ <string name="user_add_user_message_update" msgid="7061671307004867811">"Svaki korisnik može ažurirati aplikacije za ostale korisnike."</string> <string name="car_loading_profile" msgid="4507385037552574474">"Učitavanje"</string> <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Učitavanje korisnika (od <xliff:g id="FROM_USER">%1$d</xliff:g> do <xliff:g id="TO_USER">%2$d</xliff:g>)"</string> + <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) --> + <skip /> </resources> diff --git a/packages/CarSystemUI/res/values-hu/strings.xml b/packages/CarSystemUI/res/values-hu/strings.xml index ffa512c4772a..b63ba8b8ed60 100644 --- a/packages/CarSystemUI/res/values-hu/strings.xml +++ b/packages/CarSystemUI/res/values-hu/strings.xml @@ -28,4 +28,6 @@ <string name="user_add_user_message_update" msgid="7061671307004867811">"Bármely felhasználó frissítheti az alkalmazásokat az összes felhasználó számára."</string> <string name="car_loading_profile" msgid="4507385037552574474">"Betöltés"</string> <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Felhasználó betöltése (<xliff:g id="FROM_USER">%1$d</xliff:g> → <xliff:g id="TO_USER">%2$d</xliff:g>)"</string> + <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) --> + <skip /> </resources> diff --git a/packages/CarSystemUI/res/values-hy/strings.xml b/packages/CarSystemUI/res/values-hy/strings.xml index ee6f74b4e5d8..e2a2c6bf459c 100644 --- a/packages/CarSystemUI/res/values-hy/strings.xml +++ b/packages/CarSystemUI/res/values-hy/strings.xml @@ -28,4 +28,6 @@ <string name="user_add_user_message_update" msgid="7061671307004867811">"Ցանկացած օգտատեր կարող է թարմացնել հավելվածները բոլոր մյուս հաշիվների համար։"</string> <string name="car_loading_profile" msgid="4507385037552574474">"Բեռնում"</string> <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Օգտատերը բեռնվում է (<xliff:g id="FROM_USER">%1$d</xliff:g> – <xliff:g id="TO_USER">%2$d</xliff:g>)"</string> + <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) --> + <skip /> </resources> diff --git a/packages/CarSystemUI/res/values-in/strings.xml b/packages/CarSystemUI/res/values-in/strings.xml index 901cbe759d63..0a70d261b77d 100644 --- a/packages/CarSystemUI/res/values-in/strings.xml +++ b/packages/CarSystemUI/res/values-in/strings.xml @@ -28,4 +28,6 @@ <string name="user_add_user_message_update" msgid="7061671307004867811">"Setiap pengguna dapat mengupdate aplikasi untuk semua pengguna lain."</string> <string name="car_loading_profile" msgid="4507385037552574474">"Memuat"</string> <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Memuat pengguna (dari <xliff:g id="FROM_USER">%1$d</xliff:g> menjadi <xliff:g id="TO_USER">%2$d</xliff:g>)"</string> + <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) --> + <skip /> </resources> diff --git a/packages/CarSystemUI/res/values-is/strings.xml b/packages/CarSystemUI/res/values-is/strings.xml index 13e71366ebd3..ea6b031c72b9 100644 --- a/packages/CarSystemUI/res/values-is/strings.xml +++ b/packages/CarSystemUI/res/values-is/strings.xml @@ -28,4 +28,6 @@ <string name="user_add_user_message_update" msgid="7061671307004867811">"Allir notendur geta uppfært forrit fyrir alla aðra notendur."</string> <string name="car_loading_profile" msgid="4507385037552574474">"Hleður"</string> <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Hleður notanda (frá <xliff:g id="FROM_USER">%1$d</xliff:g> til <xliff:g id="TO_USER">%2$d</xliff:g>)"</string> + <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) --> + <skip /> </resources> diff --git a/packages/CarSystemUI/res/values-it/strings.xml b/packages/CarSystemUI/res/values-it/strings.xml index f4f7ab719661..ecbcd5d9dadd 100644 --- a/packages/CarSystemUI/res/values-it/strings.xml +++ b/packages/CarSystemUI/res/values-it/strings.xml @@ -28,4 +28,6 @@ <string name="user_add_user_message_update" msgid="7061671307004867811">"Qualsiasi utente può aggiornare le app per tutti gli altri."</string> <string name="car_loading_profile" msgid="4507385037552574474">"Caricamento"</string> <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Caricamento dell\'utente (da <xliff:g id="FROM_USER">%1$d</xliff:g> a <xliff:g id="TO_USER">%2$d</xliff:g>)"</string> + <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) --> + <skip /> </resources> diff --git a/packages/CarSystemUI/res/values-iw/strings.xml b/packages/CarSystemUI/res/values-iw/strings.xml index a044b23709e1..fe182a3b7251 100644 --- a/packages/CarSystemUI/res/values-iw/strings.xml +++ b/packages/CarSystemUI/res/values-iw/strings.xml @@ -28,4 +28,6 @@ <string name="user_add_user_message_update" msgid="7061671307004867811">"כל משתמש יכול לעדכן אפליקציות לכל שאר המשתמשים."</string> <string name="car_loading_profile" msgid="4507385037552574474">"בטעינה"</string> <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"המשתמש בטעינה (מהמשתמש <xliff:g id="FROM_USER">%1$d</xliff:g> אל <xliff:g id="TO_USER">%2$d</xliff:g>)"</string> + <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) --> + <skip /> </resources> diff --git a/packages/CarSystemUI/res/values-ja/strings.xml b/packages/CarSystemUI/res/values-ja/strings.xml index aae7dbff155e..14486758dcd1 100644 --- a/packages/CarSystemUI/res/values-ja/strings.xml +++ b/packages/CarSystemUI/res/values-ja/strings.xml @@ -28,4 +28,6 @@ <string name="user_add_user_message_update" msgid="7061671307004867811">"どのユーザーも他のすべてのユーザーに代わってアプリを更新できます。"</string> <string name="car_loading_profile" msgid="4507385037552574474">"読み込んでいます"</string> <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"ユーザーを読み込んでいます(<xliff:g id="FROM_USER">%1$d</xliff:g>~<xliff:g id="TO_USER">%2$d</xliff:g>)"</string> + <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) --> + <skip /> </resources> diff --git a/packages/CarSystemUI/res/values-ka/strings.xml b/packages/CarSystemUI/res/values-ka/strings.xml index 19894d164189..0fef7e55f27e 100644 --- a/packages/CarSystemUI/res/values-ka/strings.xml +++ b/packages/CarSystemUI/res/values-ka/strings.xml @@ -28,4 +28,6 @@ <string name="user_add_user_message_update" msgid="7061671307004867811">"ნებისმიერ მომხმარებელს შეუძლია აპები ყველა სხვა მომხმარებლისათვის განაახლოს."</string> <string name="car_loading_profile" msgid="4507385037552574474">"იტვირთება"</string> <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"იტვირთება მომხმარებელი (<xliff:g id="FROM_USER">%1$d</xliff:g>-დან <xliff:g id="TO_USER">%2$d</xliff:g>-მდე)"</string> + <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) --> + <skip /> </resources> diff --git a/packages/CarSystemUI/res/values-kk/strings.xml b/packages/CarSystemUI/res/values-kk/strings.xml index f9449be39540..a4cf78709515 100644 --- a/packages/CarSystemUI/res/values-kk/strings.xml +++ b/packages/CarSystemUI/res/values-kk/strings.xml @@ -28,4 +28,6 @@ <string name="user_add_user_message_update" msgid="7061671307004867811">"Кез келген пайдаланушы қолданбаларды басқа пайдаланушылар үшін жаңарта алады."</string> <string name="car_loading_profile" msgid="4507385037552574474">"Жүктелуде"</string> <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Пайдаланушы профилі жүктелуде (<xliff:g id="FROM_USER">%1$d</xliff:g> – <xliff:g id="TO_USER">%2$d</xliff:g>)"</string> + <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) --> + <skip /> </resources> diff --git a/packages/CarSystemUI/res/values-km/strings.xml b/packages/CarSystemUI/res/values-km/strings.xml index fbcab8431476..7b9a093d7f39 100644 --- a/packages/CarSystemUI/res/values-km/strings.xml +++ b/packages/CarSystemUI/res/values-km/strings.xml @@ -28,4 +28,6 @@ <string name="user_add_user_message_update" msgid="7061671307004867811">"អ្នកប្រើប្រាស់ណាក៏អាចដំឡើងកំណែកម្មវិធីសម្រាប់អ្នកប្រើប្រាស់ទាំងអស់ផ្សេងទៀតបានដែរ។"</string> <string name="car_loading_profile" msgid="4507385037552574474">"កំពុងផ្ទុក"</string> <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"កំពុងផ្ទុកអ្នកប្រើប្រាស់ (ពី <xliff:g id="FROM_USER">%1$d</xliff:g> ដល់ <xliff:g id="TO_USER">%2$d</xliff:g>)"</string> + <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) --> + <skip /> </resources> diff --git a/packages/CarSystemUI/res/values-kn/strings.xml b/packages/CarSystemUI/res/values-kn/strings.xml index 21c4cc02e207..34a83553d5c0 100644 --- a/packages/CarSystemUI/res/values-kn/strings.xml +++ b/packages/CarSystemUI/res/values-kn/strings.xml @@ -28,4 +28,6 @@ <string name="user_add_user_message_update" msgid="7061671307004867811">"ಯಾವುದೇ ಬಳಕೆದಾರರು ಎಲ್ಲಾ ಇತರೆ ಬಳಕೆದಾರರಿಗಾಗಿ ಆ್ಯಪ್ಗಳನ್ನು ಅಪ್ಡೇಟ್ ಮಾಡಬಹುದು."</string> <string name="car_loading_profile" msgid="4507385037552574474">"ಲೋಡ್ ಆಗುತ್ತಿದೆ"</string> <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"ಬಳಕೆದಾರರನ್ನು ಲೋಡ್ ಮಾಡಲಾಗುತ್ತಿದೆ (<xliff:g id="FROM_USER">%1$d</xliff:g> ನಿಂದ <xliff:g id="TO_USER">%2$d</xliff:g> ವರೆಗೆ)"</string> + <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) --> + <skip /> </resources> diff --git a/packages/CarSystemUI/res/values-ko/strings.xml b/packages/CarSystemUI/res/values-ko/strings.xml index 6b670b018814..b570c5c6b81e 100644 --- a/packages/CarSystemUI/res/values-ko/strings.xml +++ b/packages/CarSystemUI/res/values-ko/strings.xml @@ -28,4 +28,6 @@ <string name="user_add_user_message_update" msgid="7061671307004867811">"누구나 다른 모든 사용자를 위해 앱을 업데이트할 수 있습니다."</string> <string name="car_loading_profile" msgid="4507385037552574474">"로드 중"</string> <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"사용자 로드 중(<xliff:g id="FROM_USER">%1$d</xliff:g>님에서 <xliff:g id="TO_USER">%2$d</xliff:g>님으로)"</string> + <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) --> + <skip /> </resources> diff --git a/packages/CarSystemUI/res/values-ky/strings.xml b/packages/CarSystemUI/res/values-ky/strings.xml index b093363d0c23..c66b34ffb9c1 100644 --- a/packages/CarSystemUI/res/values-ky/strings.xml +++ b/packages/CarSystemUI/res/values-ky/strings.xml @@ -28,4 +28,6 @@ <string name="user_add_user_message_update" msgid="7061671307004867811">"Колдонмолорду бир колдонуучу калган бардык колдонуучулар үчүн да жаңырта алат."</string> <string name="car_loading_profile" msgid="4507385037552574474">"Жүктөлүүдө"</string> <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Колдонуучу тууралуу маалымат жүктөлүүдө (<xliff:g id="FROM_USER">%1$d</xliff:g> – <xliff:g id="TO_USER">%2$d</xliff:g>)"</string> + <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) --> + <skip /> </resources> diff --git a/packages/CarSystemUI/res/values-lo/strings.xml b/packages/CarSystemUI/res/values-lo/strings.xml index d2199e2a0cef..2bf19e03b82a 100644 --- a/packages/CarSystemUI/res/values-lo/strings.xml +++ b/packages/CarSystemUI/res/values-lo/strings.xml @@ -28,4 +28,6 @@ <string name="user_add_user_message_update" msgid="7061671307004867811">"ຜູ້ໃຊ້ຕ່າງໆສາມາດອັບເດດແອັບສຳລັບຜູ້ໃຊ້ອື່ນທັງໝົດໄດ້."</string> <string name="car_loading_profile" msgid="4507385037552574474">"ກຳລັງໂຫຼດ"</string> <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"ກຳລັງໂຫຼດຜູ້ໃຊ້ (ຈາກ <xliff:g id="FROM_USER">%1$d</xliff:g> ໄປຍັງ <xliff:g id="TO_USER">%2$d</xliff:g>)"</string> + <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) --> + <skip /> </resources> diff --git a/packages/CarSystemUI/res/values-lt/strings.xml b/packages/CarSystemUI/res/values-lt/strings.xml index 26690002416e..1cae1e907193 100644 --- a/packages/CarSystemUI/res/values-lt/strings.xml +++ b/packages/CarSystemUI/res/values-lt/strings.xml @@ -28,4 +28,6 @@ <string name="user_add_user_message_update" msgid="7061671307004867811">"Bet kuris naudotojas gali atnaujinti visų kitų naudotojų programas."</string> <string name="car_loading_profile" msgid="4507385037552574474">"Įkeliama"</string> <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Įkeliamas naudotojo profilis (<xliff:g id="FROM_USER">%1$d</xliff:g> – <xliff:g id="TO_USER">%2$d</xliff:g>)"</string> + <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) --> + <skip /> </resources> diff --git a/packages/CarSystemUI/res/values-lv/strings.xml b/packages/CarSystemUI/res/values-lv/strings.xml index 87b4ede4383a..62b8bf8d98eb 100644 --- a/packages/CarSystemUI/res/values-lv/strings.xml +++ b/packages/CarSystemUI/res/values-lv/strings.xml @@ -28,4 +28,6 @@ <string name="user_add_user_message_update" msgid="7061671307004867811">"Ikviens lietotājs var atjaunināt lietotnes visu lietotāju vārdā."</string> <string name="car_loading_profile" msgid="4507385037552574474">"Notiek ielāde…"</string> <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Notiek lietotāja profila ielāde (<xliff:g id="FROM_USER">%1$d</xliff:g>–<xliff:g id="TO_USER">%2$d</xliff:g>)…"</string> + <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) --> + <skip /> </resources> diff --git a/packages/CarSystemUI/res/values-mk/strings.xml b/packages/CarSystemUI/res/values-mk/strings.xml index f8fd02cb071d..3e7ad63ae2d9 100644 --- a/packages/CarSystemUI/res/values-mk/strings.xml +++ b/packages/CarSystemUI/res/values-mk/strings.xml @@ -28,4 +28,6 @@ <string name="user_add_user_message_update" msgid="7061671307004867811">"Секој корисник може да ажурира апликации за сите други корисници."</string> <string name="car_loading_profile" msgid="4507385037552574474">"Се вчитува"</string> <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Се вчитува корисникот (од <xliff:g id="FROM_USER">%1$d</xliff:g> до <xliff:g id="TO_USER">%2$d</xliff:g>)"</string> + <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) --> + <skip /> </resources> diff --git a/packages/CarSystemUI/res/values-ml/strings.xml b/packages/CarSystemUI/res/values-ml/strings.xml index 343157160105..3bc7557b5484 100644 --- a/packages/CarSystemUI/res/values-ml/strings.xml +++ b/packages/CarSystemUI/res/values-ml/strings.xml @@ -28,4 +28,6 @@ <string name="user_add_user_message_update" msgid="7061671307004867811">"ഏതൊരു ഉപയോക്താവിനും മറ്റെല്ലാ ഉപയോക്താക്കൾക്കുമായി ആപ്പുകൾ അപ്ഡേറ്റ് ചെയ്യാനാവും."</string> <string name="car_loading_profile" msgid="4507385037552574474">"ലോഡ് ചെയ്യുന്നു"</string> <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"ഉപയോക്തൃ പ്രൊഫൈൽ ലോഡ് ചെയ്യുന്നു (<xliff:g id="FROM_USER">%1$d</xliff:g> എന്നതിൽ നിന്ന് <xliff:g id="TO_USER">%2$d</xliff:g> എന്നതിലേക്ക്)"</string> + <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) --> + <skip /> </resources> diff --git a/packages/CarSystemUI/res/values-mn/strings.xml b/packages/CarSystemUI/res/values-mn/strings.xml index 5bd76dc7e067..45921d26172e 100644 --- a/packages/CarSystemUI/res/values-mn/strings.xml +++ b/packages/CarSystemUI/res/values-mn/strings.xml @@ -28,4 +28,6 @@ <string name="user_add_user_message_update" msgid="7061671307004867811">"Бусад бүх хэрэглэгчийн аппыг дурын хэрэглэгч шинэчлэх боломжтой."</string> <string name="car_loading_profile" msgid="4507385037552574474">"Ачаалж байна"</string> <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Хэрэглэгчийг ачаалж байна (<xliff:g id="FROM_USER">%1$d</xliff:g>-с <xliff:g id="TO_USER">%2$d</xliff:g> хүртэл)"</string> + <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) --> + <skip /> </resources> diff --git a/packages/CarSystemUI/res/values-mr/strings.xml b/packages/CarSystemUI/res/values-mr/strings.xml index 2366465e1a9f..fdbab4fbc4f2 100644 --- a/packages/CarSystemUI/res/values-mr/strings.xml +++ b/packages/CarSystemUI/res/values-mr/strings.xml @@ -28,4 +28,6 @@ <string name="user_add_user_message_update" msgid="7061671307004867811">"कोणताही वापरकर्ता इतर सर्व वापरकर्त्यांसाठी अॅप्स अपडेट करू शकतो."</string> <string name="car_loading_profile" msgid="4507385037552574474">"लोड करत आहे"</string> <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"वापरकर्ता लोड करत आहे (<xliff:g id="FROM_USER">%1$d</xliff:g> पासून <xliff:g id="TO_USER">%2$d</xliff:g> पर्यंत)"</string> + <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) --> + <skip /> </resources> diff --git a/packages/CarSystemUI/res/values-ms/strings.xml b/packages/CarSystemUI/res/values-ms/strings.xml index 29ac83afc12c..1a43d9c7cdef 100644 --- a/packages/CarSystemUI/res/values-ms/strings.xml +++ b/packages/CarSystemUI/res/values-ms/strings.xml @@ -28,4 +28,6 @@ <string name="user_add_user_message_update" msgid="7061671307004867811">"Mana-mana pengguna boleh mengemas kini apl untuk semua pengguna lain."</string> <string name="car_loading_profile" msgid="4507385037552574474">"Memuatkan"</string> <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Memuatkan pengguna (daripada <xliff:g id="FROM_USER">%1$d</xliff:g> hingga <xliff:g id="TO_USER">%2$d</xliff:g>)"</string> + <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) --> + <skip /> </resources> diff --git a/packages/CarSystemUI/res/values-my/strings.xml b/packages/CarSystemUI/res/values-my/strings.xml index 7f240b430471..4f3922b373b5 100644 --- a/packages/CarSystemUI/res/values-my/strings.xml +++ b/packages/CarSystemUI/res/values-my/strings.xml @@ -28,4 +28,6 @@ <string name="user_add_user_message_update" msgid="7061671307004867811">"မည်သူမဆို အသုံးပြုသူအားလုံးအတွက် အက်ပ်များကို အပ်ဒိတ်လုပ်နိုင်သည်။"</string> <string name="car_loading_profile" msgid="4507385037552574474">"ဖွင့်နေသည်"</string> <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"အသုံးပြုသူကို ဖွင့်နေသည် (<xliff:g id="FROM_USER">%1$d</xliff:g> မှ <xliff:g id="TO_USER">%2$d</xliff:g> သို့)"</string> + <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) --> + <skip /> </resources> diff --git a/packages/CarSystemUI/res/values-nb/strings.xml b/packages/CarSystemUI/res/values-nb/strings.xml index c1cfcf727859..5b6166feba0f 100644 --- a/packages/CarSystemUI/res/values-nb/strings.xml +++ b/packages/CarSystemUI/res/values-nb/strings.xml @@ -28,4 +28,6 @@ <string name="user_add_user_message_update" msgid="7061671307004867811">"Alle brukere kan oppdatere apper for alle andre brukere."</string> <string name="car_loading_profile" msgid="4507385037552574474">"Laster inn"</string> <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Laster inn brukeren (fra <xliff:g id="FROM_USER">%1$d</xliff:g> til <xliff:g id="TO_USER">%2$d</xliff:g>)"</string> + <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) --> + <skip /> </resources> diff --git a/packages/CarSystemUI/res/values-ne/strings.xml b/packages/CarSystemUI/res/values-ne/strings.xml index 70bcfc763cf2..e9eb4d8bdac7 100644 --- a/packages/CarSystemUI/res/values-ne/strings.xml +++ b/packages/CarSystemUI/res/values-ne/strings.xml @@ -28,4 +28,6 @@ <string name="user_add_user_message_update" msgid="7061671307004867811">"सबै प्रयोगकर्ताले अन्य प्रयोगकर्ताका अनुप्रयोगहरू अद्यावधिक गर्न सक्छन्।"</string> <string name="car_loading_profile" msgid="4507385037552574474">"लोड गरिँदै"</string> <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"प्रयोगकर्तासम्बन्धी जानकारी लोड गरिँदै (<xliff:g id="FROM_USER">%1$d</xliff:g> बाट <xliff:g id="TO_USER">%2$d</xliff:g> मा)"</string> + <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) --> + <skip /> </resources> diff --git a/packages/CarSystemUI/res/values-nl/strings.xml b/packages/CarSystemUI/res/values-nl/strings.xml index 95a76467b078..d79f2b1d10f4 100644 --- a/packages/CarSystemUI/res/values-nl/strings.xml +++ b/packages/CarSystemUI/res/values-nl/strings.xml @@ -28,4 +28,6 @@ <string name="user_add_user_message_update" msgid="7061671307004867811">"Elke gebruiker kan apps updaten voor alle andere gebruikers"</string> <string name="car_loading_profile" msgid="4507385037552574474">"Laden"</string> <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Gebruiker laden (van <xliff:g id="FROM_USER">%1$d</xliff:g> naar <xliff:g id="TO_USER">%2$d</xliff:g>)"</string> + <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) --> + <skip /> </resources> diff --git a/packages/CarSystemUI/res/values-or/strings.xml b/packages/CarSystemUI/res/values-or/strings.xml index b0b59b348594..58f59e4c4dbf 100644 --- a/packages/CarSystemUI/res/values-or/strings.xml +++ b/packages/CarSystemUI/res/values-or/strings.xml @@ -28,4 +28,6 @@ <string name="user_add_user_message_update" msgid="7061671307004867811">"ଯେ କୌଣସି ଉପଯୋଗକର୍ତ୍ତା ଅନ୍ୟ ସମସ୍ତ ଉପଯୋଗକର୍ତ୍ତାଙ୍କ ପାଇଁ ଆପଗୁଡ଼ିକୁ ଅପଡେଟ୍ କରିପାରିବେ।"</string> <string name="car_loading_profile" msgid="4507385037552574474">"ଲୋଡ୍ କରାଯାଉଛି"</string> <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"ଉପଯୋଗକର୍ତ୍ତାଙ୍କୁ ଲୋଡ୍ କରାଯାଉଛି (<xliff:g id="FROM_USER">%1$d</xliff:g>ଙ୍କ ଠାରୁ <xliff:g id="TO_USER">%2$d</xliff:g> ପର୍ଯ୍ୟନ୍ତ)"</string> + <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) --> + <skip /> </resources> diff --git a/packages/CarSystemUI/res/values-pa/strings.xml b/packages/CarSystemUI/res/values-pa/strings.xml index e7efca1894b7..e73e20a5fc85 100644 --- a/packages/CarSystemUI/res/values-pa/strings.xml +++ b/packages/CarSystemUI/res/values-pa/strings.xml @@ -28,4 +28,6 @@ <string name="user_add_user_message_update" msgid="7061671307004867811">"ਕੋਈ ਵੀ ਵਰਤੋਂਕਾਰ ਹੋਰ ਸਾਰੇ ਵਰਤੋਂਕਾਰਾਂ ਦੀਆਂ ਐਪਾਂ ਨੂੰ ਅੱਪਡੇਟ ਕਰ ਸਕਦਾ ਹੈ।"</string> <string name="car_loading_profile" msgid="4507385037552574474">"ਲੋਡ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ"</string> <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"ਵਰਤੋਂਕਾਰ ਨੂੰ ਲੋਡ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ (<xliff:g id="FROM_USER">%1$d</xliff:g> ਤੋਂ <xliff:g id="TO_USER">%2$d</xliff:g> ਤੱਕ)"</string> + <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) --> + <skip /> </resources> diff --git a/packages/CarSystemUI/res/values-pl/strings.xml b/packages/CarSystemUI/res/values-pl/strings.xml index 37d0ef171599..dd8c1892b63e 100644 --- a/packages/CarSystemUI/res/values-pl/strings.xml +++ b/packages/CarSystemUI/res/values-pl/strings.xml @@ -28,4 +28,6 @@ <string name="user_add_user_message_update" msgid="7061671307004867811">"Każdy użytkownik może aktualizować aplikacje wszystkich innych użytkowników."</string> <string name="car_loading_profile" msgid="4507385037552574474">"Ładuję"</string> <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Ładuję użytkownika (od <xliff:g id="FROM_USER">%1$d</xliff:g> do <xliff:g id="TO_USER">%2$d</xliff:g>)"</string> + <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) --> + <skip /> </resources> diff --git a/packages/CarSystemUI/res/values-pt-rPT/strings.xml b/packages/CarSystemUI/res/values-pt-rPT/strings.xml index 182f5b58d1b1..c7f5ecf00707 100644 --- a/packages/CarSystemUI/res/values-pt-rPT/strings.xml +++ b/packages/CarSystemUI/res/values-pt-rPT/strings.xml @@ -28,4 +28,6 @@ <string name="user_add_user_message_update" msgid="7061671307004867811">"Qualquer utilizador pode atualizar apps para todos os outros utilizadores."</string> <string name="car_loading_profile" msgid="4507385037552574474">"A carregar…"</string> <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"A carregar o utilizador (de <xliff:g id="FROM_USER">%1$d</xliff:g> para <xliff:g id="TO_USER">%2$d</xliff:g>)…"</string> + <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) --> + <skip /> </resources> diff --git a/packages/CarSystemUI/res/values-pt/strings.xml b/packages/CarSystemUI/res/values-pt/strings.xml index 5c8394a51599..0712fb82f7fd 100644 --- a/packages/CarSystemUI/res/values-pt/strings.xml +++ b/packages/CarSystemUI/res/values-pt/strings.xml @@ -28,4 +28,6 @@ <string name="user_add_user_message_update" msgid="7061671307004867811">"Qualquer usuário pode atualizar apps para os demais usuários."</string> <string name="car_loading_profile" msgid="4507385037552574474">"Carregando"</string> <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Carregando usuário (de <xliff:g id="FROM_USER">%1$d</xliff:g> para <xliff:g id="TO_USER">%2$d</xliff:g>)"</string> + <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) --> + <skip /> </resources> diff --git a/packages/CarSystemUI/res/values-ro/strings.xml b/packages/CarSystemUI/res/values-ro/strings.xml index 25ecbb6453b0..60fd4fef41c0 100644 --- a/packages/CarSystemUI/res/values-ro/strings.xml +++ b/packages/CarSystemUI/res/values-ro/strings.xml @@ -28,4 +28,6 @@ <string name="user_add_user_message_update" msgid="7061671307004867811">"Orice utilizator poate actualiza aplicațiile pentru toți ceilalți utilizatori."</string> <string name="car_loading_profile" msgid="4507385037552574474">"Se încarcă"</string> <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Se încarcă utilizatorul (de la <xliff:g id="FROM_USER">%1$d</xliff:g> la <xliff:g id="TO_USER">%2$d</xliff:g>)"</string> + <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) --> + <skip /> </resources> diff --git a/packages/CarSystemUI/res/values-ru/strings.xml b/packages/CarSystemUI/res/values-ru/strings.xml index e93d25d1acdc..a043d24789a9 100644 --- a/packages/CarSystemUI/res/values-ru/strings.xml +++ b/packages/CarSystemUI/res/values-ru/strings.xml @@ -28,4 +28,6 @@ <string name="user_add_user_message_update" msgid="7061671307004867811">"Любой пользователь устройства может обновлять приложения для всех аккаунтов."</string> <string name="car_loading_profile" msgid="4507385037552574474">"Загрузка…"</string> <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Загрузка профиля пользователя (с <xliff:g id="FROM_USER">%1$d</xliff:g> по <xliff:g id="TO_USER">%2$d</xliff:g>)…"</string> + <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) --> + <skip /> </resources> diff --git a/packages/CarSystemUI/res/values-si/strings.xml b/packages/CarSystemUI/res/values-si/strings.xml index 947cb0a53206..e14f64a7bca8 100644 --- a/packages/CarSystemUI/res/values-si/strings.xml +++ b/packages/CarSystemUI/res/values-si/strings.xml @@ -28,4 +28,6 @@ <string name="user_add_user_message_update" msgid="7061671307004867811">"සියලුම අනෙක් පරිශීලකයින් සඳහා ඕනෑම පරිශීලකයෙකුට යෙදුම් යාවත්කාලීන කළ හැක."</string> <string name="car_loading_profile" msgid="4507385037552574474">"පූරණය වෙමින්"</string> <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"පරිශීලකයා පූරණය වෙමින් (<xliff:g id="FROM_USER">%1$d</xliff:g> සිට <xliff:g id="TO_USER">%2$d</xliff:g> වෙත)"</string> + <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) --> + <skip /> </resources> diff --git a/packages/CarSystemUI/res/values-sk/strings.xml b/packages/CarSystemUI/res/values-sk/strings.xml index ea99f0faf5e0..8f98983877d9 100644 --- a/packages/CarSystemUI/res/values-sk/strings.xml +++ b/packages/CarSystemUI/res/values-sk/strings.xml @@ -28,4 +28,6 @@ <string name="user_add_user_message_update" msgid="7061671307004867811">"Ktorýkoľvek používateľ môže aktualizovať aplikácie všetkých ostatných používateľov."</string> <string name="car_loading_profile" msgid="4507385037552574474">"Načítava sa"</string> <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Načítava sa používateľ (predchádzajúci: <xliff:g id="FROM_USER">%1$d</xliff:g>, nasledujúci: <xliff:g id="TO_USER">%2$d</xliff:g>)"</string> + <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) --> + <skip /> </resources> diff --git a/packages/CarSystemUI/res/values-sl/strings.xml b/packages/CarSystemUI/res/values-sl/strings.xml index 32fd50e9cb8b..6b471845b657 100644 --- a/packages/CarSystemUI/res/values-sl/strings.xml +++ b/packages/CarSystemUI/res/values-sl/strings.xml @@ -28,4 +28,6 @@ <string name="user_add_user_message_update" msgid="7061671307004867811">"Vsak uporabnik lahko posodobi aplikacije za vse druge uporabnike."</string> <string name="car_loading_profile" msgid="4507385037552574474">"Nalaganje"</string> <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Nalaganje uporabnika (od uporabnika <xliff:g id="FROM_USER">%1$d</xliff:g> do uporabnika <xliff:g id="TO_USER">%2$d</xliff:g>)"</string> + <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) --> + <skip /> </resources> diff --git a/packages/CarSystemUI/res/values-sq/strings.xml b/packages/CarSystemUI/res/values-sq/strings.xml index 6fdd06c7d4d6..18ea40131817 100644 --- a/packages/CarSystemUI/res/values-sq/strings.xml +++ b/packages/CarSystemUI/res/values-sq/strings.xml @@ -28,4 +28,6 @@ <string name="user_add_user_message_update" msgid="7061671307004867811">"Çdo përdorues mund t\'i përditësojë aplikacionet për të gjithë përdoruesit e tjerë."</string> <string name="car_loading_profile" msgid="4507385037552574474">"Po ngarkohet"</string> <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Përdoruesi po ngarkohet (nga <xliff:g id="FROM_USER">%1$d</xliff:g> te <xliff:g id="TO_USER">%2$d</xliff:g>)"</string> + <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) --> + <skip /> </resources> diff --git a/packages/CarSystemUI/res/values-sr/strings.xml b/packages/CarSystemUI/res/values-sr/strings.xml index 494aeaa0f1f9..878a1c1aa425 100644 --- a/packages/CarSystemUI/res/values-sr/strings.xml +++ b/packages/CarSystemUI/res/values-sr/strings.xml @@ -28,4 +28,6 @@ <string name="user_add_user_message_update" msgid="7061671307004867811">"Сваки корисник може да ажурира апликације за све остале кориснике."</string> <string name="car_loading_profile" msgid="4507385037552574474">"Учитава се"</string> <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Профил корисника се учитава (из<xliff:g id="FROM_USER">%1$d</xliff:g> у <xliff:g id="TO_USER">%2$d</xliff:g>)"</string> + <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) --> + <skip /> </resources> diff --git a/packages/CarSystemUI/res/values-sv/strings.xml b/packages/CarSystemUI/res/values-sv/strings.xml index 65481cdf2533..905dd445eeab 100644 --- a/packages/CarSystemUI/res/values-sv/strings.xml +++ b/packages/CarSystemUI/res/values-sv/strings.xml @@ -28,4 +28,6 @@ <string name="user_add_user_message_update" msgid="7061671307004867811">"Alla användare kan uppdatera appar för andra användare."</string> <string name="car_loading_profile" msgid="4507385037552574474">"Läser in"</string> <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Läser in användare (från <xliff:g id="FROM_USER">%1$d</xliff:g> till <xliff:g id="TO_USER">%2$d</xliff:g>)"</string> + <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) --> + <skip /> </resources> diff --git a/packages/CarSystemUI/res/values-sw/strings.xml b/packages/CarSystemUI/res/values-sw/strings.xml index a79d628e6786..3cb6098a3f1b 100644 --- a/packages/CarSystemUI/res/values-sw/strings.xml +++ b/packages/CarSystemUI/res/values-sw/strings.xml @@ -28,4 +28,6 @@ <string name="user_add_user_message_update" msgid="7061671307004867811">"Mtumiaji yeyote anaweza kusasisha programu za watumiaji wengine."</string> <string name="car_loading_profile" msgid="4507385037552574474">"Inapakia"</string> <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Inapakia wasifu wa mtumiaji (kutoka <xliff:g id="FROM_USER">%1$d</xliff:g> kuwa <xliff:g id="TO_USER">%2$d</xliff:g>)"</string> + <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) --> + <skip /> </resources> diff --git a/packages/CarSystemUI/res/values-ta/strings.xml b/packages/CarSystemUI/res/values-ta/strings.xml index 849b40d36ccd..de52edee1621 100644 --- a/packages/CarSystemUI/res/values-ta/strings.xml +++ b/packages/CarSystemUI/res/values-ta/strings.xml @@ -28,4 +28,6 @@ <string name="user_add_user_message_update" msgid="7061671307004867811">"எந்தப் பயனரும் பிற பயனர்கள் சார்பாக ஆப்ஸைப் புதுப்பிக்க முடியும்."</string> <string name="car_loading_profile" msgid="4507385037552574474">"ஏற்றுகிறது"</string> <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"பயனர் தகவலை ஏற்றுகிறது (<xliff:g id="FROM_USER">%1$d</xliff:g>லிருந்து <xliff:g id="TO_USER">%2$d</xliff:g> வரை)"</string> + <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) --> + <skip /> </resources> diff --git a/packages/CarSystemUI/res/values-te/strings.xml b/packages/CarSystemUI/res/values-te/strings.xml index 83bb24a64a46..fff0845dfb6c 100644 --- a/packages/CarSystemUI/res/values-te/strings.xml +++ b/packages/CarSystemUI/res/values-te/strings.xml @@ -28,4 +28,6 @@ <string name="user_add_user_message_update" msgid="7061671307004867811">"ఏ యూజర్ అయినా మిగతా యూజర్ల కోసం యాప్లను అప్డేట్ చేయవచ్చు."</string> <string name="car_loading_profile" msgid="4507385037552574474">"లోడ్ అవుతోంది"</string> <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"యూజర్ను లోడ్ చేస్తోంది (<xliff:g id="FROM_USER">%1$d</xliff:g> నుండి <xliff:g id="TO_USER">%2$d</xliff:g> వరకు)"</string> + <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) --> + <skip /> </resources> diff --git a/packages/CarSystemUI/res/values-th/strings.xml b/packages/CarSystemUI/res/values-th/strings.xml index fa99ac1928c1..8f3012b5b4a5 100644 --- a/packages/CarSystemUI/res/values-th/strings.xml +++ b/packages/CarSystemUI/res/values-th/strings.xml @@ -28,4 +28,6 @@ <string name="user_add_user_message_update" msgid="7061671307004867811">"ผู้ใช้ทุกคนจะอัปเดตแอปให้แก่ผู้ใช้คนอื่นๆ ได้"</string> <string name="car_loading_profile" msgid="4507385037552574474">"กำลังโหลด"</string> <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"กำลังโหลดผู้ใช้ (จาก <xliff:g id="FROM_USER">%1$d</xliff:g> ถึง <xliff:g id="TO_USER">%2$d</xliff:g>)"</string> + <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) --> + <skip /> </resources> diff --git a/packages/CarSystemUI/res/values-tl/strings.xml b/packages/CarSystemUI/res/values-tl/strings.xml index c6f5f59acf0d..5b0e3b3cf19c 100644 --- a/packages/CarSystemUI/res/values-tl/strings.xml +++ b/packages/CarSystemUI/res/values-tl/strings.xml @@ -28,4 +28,6 @@ <string name="user_add_user_message_update" msgid="7061671307004867811">"Puwedeng i-update ng sinumang user ang mga app para sa lahat ng iba pang user."</string> <string name="car_loading_profile" msgid="4507385037552574474">"Naglo-load"</string> <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Nilo-load ang user (mula kay <xliff:g id="FROM_USER">%1$d</xliff:g> papunta kay <xliff:g id="TO_USER">%2$d</xliff:g>)"</string> + <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) --> + <skip /> </resources> diff --git a/packages/CarSystemUI/res/values-tr/strings.xml b/packages/CarSystemUI/res/values-tr/strings.xml index a76812769dae..81fa01c16058 100644 --- a/packages/CarSystemUI/res/values-tr/strings.xml +++ b/packages/CarSystemUI/res/values-tr/strings.xml @@ -28,4 +28,6 @@ <string name="user_add_user_message_update" msgid="7061671307004867811">"Herhangi bir kullanıcı, diğer tüm kullanıcılar için uygulamaları güncelleyebilir."</string> <string name="car_loading_profile" msgid="4507385037552574474">"Yükleniyor"</string> <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Kullanıcı yükleniyor (<xliff:g id="FROM_USER">%1$d</xliff:g> kullanıcısından <xliff:g id="TO_USER">%2$d</xliff:g> kullanıcısına)"</string> + <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) --> + <skip /> </resources> diff --git a/packages/CarSystemUI/res/values-uk/strings.xml b/packages/CarSystemUI/res/values-uk/strings.xml index c424d87ace6e..b7031c698815 100644 --- a/packages/CarSystemUI/res/values-uk/strings.xml +++ b/packages/CarSystemUI/res/values-uk/strings.xml @@ -28,4 +28,6 @@ <string name="user_add_user_message_update" msgid="7061671307004867811">"Усі користувачі можуть оновлювати додатки для решти людей."</string> <string name="car_loading_profile" msgid="4507385037552574474">"Завантаження"</string> <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Завантаження профілю користувача (від <xliff:g id="FROM_USER">%1$d</xliff:g> до <xliff:g id="TO_USER">%2$d</xliff:g>)"</string> + <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) --> + <skip /> </resources> diff --git a/packages/CarSystemUI/res/values-ur/strings.xml b/packages/CarSystemUI/res/values-ur/strings.xml index 063dcbc4ecd1..ef65c7516956 100644 --- a/packages/CarSystemUI/res/values-ur/strings.xml +++ b/packages/CarSystemUI/res/values-ur/strings.xml @@ -28,4 +28,6 @@ <string name="user_add_user_message_update" msgid="7061671307004867811">"کوئی بھی صارف دیگر سبھی صارفین کے لیے ایپس کو اپ ڈیٹ کر سکتا ہے۔"</string> <string name="car_loading_profile" msgid="4507385037552574474">"لوڈ ہو رہی ہے"</string> <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"صارف کی نئی پروفائل لوڈ ہو رہی ہے (<xliff:g id="FROM_USER">%1$d</xliff:g> سے <xliff:g id="TO_USER">%2$d</xliff:g> کو)"</string> + <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) --> + <skip /> </resources> diff --git a/packages/CarSystemUI/res/values-uz/strings.xml b/packages/CarSystemUI/res/values-uz/strings.xml index adef2add435f..471e4591265a 100644 --- a/packages/CarSystemUI/res/values-uz/strings.xml +++ b/packages/CarSystemUI/res/values-uz/strings.xml @@ -28,4 +28,6 @@ <string name="user_add_user_message_update" msgid="7061671307004867811">"Qurilmaning istalgan foydalanuvchisi ilovalarni barcha hisoblar uchun yangilashi mumkin."</string> <string name="car_loading_profile" msgid="4507385037552574474">"Yuklanmoqda"</string> <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Foydalanuvchi profili yuklanmoqda (<xliff:g id="FROM_USER">%1$d</xliff:g> – <xliff:g id="TO_USER">%2$d</xliff:g>)"</string> + <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) --> + <skip /> </resources> diff --git a/packages/CarSystemUI/res/values-vi/strings.xml b/packages/CarSystemUI/res/values-vi/strings.xml index 616b48fd17b6..26bdddc750cd 100644 --- a/packages/CarSystemUI/res/values-vi/strings.xml +++ b/packages/CarSystemUI/res/values-vi/strings.xml @@ -28,4 +28,6 @@ <string name="user_add_user_message_update" msgid="7061671307004867811">"Bất kỳ người dùng nào cũng có thể cập nhật ứng dụng cho tất cả những người dùng khác."</string> <string name="car_loading_profile" msgid="4507385037552574474">"Đang tải"</string> <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Đang tải hồ sơ người dùng (từ <xliff:g id="FROM_USER">%1$d</xliff:g> sang <xliff:g id="TO_USER">%2$d</xliff:g>)"</string> + <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) --> + <skip /> </resources> diff --git a/packages/CarSystemUI/res/values-zh-rCN/strings.xml b/packages/CarSystemUI/res/values-zh-rCN/strings.xml index 6dc37382b25c..e7ca871337fa 100644 --- a/packages/CarSystemUI/res/values-zh-rCN/strings.xml +++ b/packages/CarSystemUI/res/values-zh-rCN/strings.xml @@ -28,4 +28,6 @@ <string name="user_add_user_message_update" msgid="7061671307004867811">"任何用户均可为所有其他用户更新应用。"</string> <string name="car_loading_profile" msgid="4507385037552574474">"正在加载"</string> <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"正在加载用户(从 <xliff:g id="FROM_USER">%1$d</xliff:g> 到 <xliff:g id="TO_USER">%2$d</xliff:g>)"</string> + <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) --> + <skip /> </resources> diff --git a/packages/CarSystemUI/res/values-zh-rHK/strings.xml b/packages/CarSystemUI/res/values-zh-rHK/strings.xml index 00ebd3219ea3..268243fea6f7 100644 --- a/packages/CarSystemUI/res/values-zh-rHK/strings.xml +++ b/packages/CarSystemUI/res/values-zh-rHK/strings.xml @@ -28,4 +28,6 @@ <string name="user_add_user_message_update" msgid="7061671307004867811">"任何使用者都可以為所有其他使用者更新應用程式。"</string> <string name="car_loading_profile" msgid="4507385037552574474">"正在載入"</string> <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"正在載入使用者 (由 <xliff:g id="FROM_USER">%1$d</xliff:g> 至 <xliff:g id="TO_USER">%2$d</xliff:g>)"</string> + <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) --> + <skip /> </resources> diff --git a/packages/CarSystemUI/res/values-zh-rTW/strings.xml b/packages/CarSystemUI/res/values-zh-rTW/strings.xml index 96d6a1d201be..9dc0f1a03d3a 100644 --- a/packages/CarSystemUI/res/values-zh-rTW/strings.xml +++ b/packages/CarSystemUI/res/values-zh-rTW/strings.xml @@ -28,4 +28,6 @@ <string name="user_add_user_message_update" msgid="7061671307004867811">"任何使用者都能為所有其他使用者更新應用程式。"</string> <string name="car_loading_profile" msgid="4507385037552574474">"載入中"</string> <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"正在載入使用者 (從 <xliff:g id="FROM_USER">%1$d</xliff:g> 到 <xliff:g id="TO_USER">%2$d</xliff:g>)"</string> + <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) --> + <skip /> </resources> diff --git a/packages/CarSystemUI/res/values-zu/strings.xml b/packages/CarSystemUI/res/values-zu/strings.xml index b835f9a69bde..8845ff71c1bb 100644 --- a/packages/CarSystemUI/res/values-zu/strings.xml +++ b/packages/CarSystemUI/res/values-zu/strings.xml @@ -28,4 +28,6 @@ <string name="user_add_user_message_update" msgid="7061671307004867811">"Noma yimuphi umsebenzisi angabuyekeza izinhlelo zokusebenza zabanye abasebenzisi."</string> <string name="car_loading_profile" msgid="4507385037552574474">"Iyalayisha"</string> <string name="car_loading_profile_developer_message" msgid="1660962766911529611">"Ilayisha umsebenzisi (kusuka ku-<xliff:g id="FROM_USER">%1$d</xliff:g> kuya ku-<xliff:g id="TO_USER">%2$d</xliff:g>)"</string> + <!-- no translation found for rear_view_camera_close_button_text (8430918817320533693) --> + <skip /> </resources> diff --git a/packages/CarSystemUI/res/values/colors.xml b/packages/CarSystemUI/res/values/colors.xml index 0181b9a39aea..91d416623538 100644 --- a/packages/CarSystemUI/res/values/colors.xml +++ b/packages/CarSystemUI/res/values/colors.xml @@ -66,4 +66,6 @@ <color name="car_user_switching_dialog_background_color">@android:color/black</color> <color name="car_user_switching_dialog_loading_text_color">@*android:color/car_body1</color> + + <color name="rear_view_camera_button_background">@*android:color/car_card_dark</color> </resources> diff --git a/packages/CarSystemUI/res/values/config.xml b/packages/CarSystemUI/res/values/config.xml index 949a0fac9598..8667ba1cd1a5 100644 --- a/packages/CarSystemUI/res/values/config.xml +++ b/packages/CarSystemUI/res/values/config.xml @@ -108,6 +108,7 @@ <item>com.android.systemui.car.keyguard.CarKeyguardViewMediator</item> <item>com.android.systemui.car.userswitcher.FullscreenUserSwitcherViewMediator</item> <item>com.android.systemui.car.userswitcher.UserSwitchTransitionViewMediator</item> + <item>com.android.systemui.car.rvc.RearViewCameraViewMediator</item> </string-array> <!-- @@ -150,4 +151,7 @@ <!-- How many milliseconds to wait before force hiding the UserSwitchTransitionView --> <integer name="config_userSwitchTransitionViewShownTimeoutMs" translatable="false">5000</integer> + + <!-- The Activity name for the Rear View Camera, if empty, the feature will be disabled. --> + <string name="config_rearViewCameraActivity" translatable="false"></string> </resources> diff --git a/packages/CarSystemUI/res/values/dimens.xml b/packages/CarSystemUI/res/values/dimens.xml index 86bfa751d085..a7d8ab5f2a4c 100644 --- a/packages/CarSystemUI/res/values/dimens.xml +++ b/packages/CarSystemUI/res/values/dimens.xml @@ -227,4 +227,8 @@ <dimen name="car_fullscreen_user_pod_image_avatar_height">96dp</dimen> <dimen name="car_user_switching_dialog_loading_text_margin_top">@*android:dimen/car_padding_4</dimen> <dimen name="car_user_switching_dialog_loading_text_font_size">@*android:dimen/car_body1_size</dimen> + + <!-- dimensions for rear view camera --> + <dimen name="rear_view_camera_width">600dp</dimen> + <dimen name="rear_view_camera_height">500dp</dimen> </resources> diff --git a/packages/CarSystemUI/res/values/strings.xml b/packages/CarSystemUI/res/values/strings.xml index 06ae7cfd6d1b..264456588c80 100644 --- a/packages/CarSystemUI/res/values/strings.xml +++ b/packages/CarSystemUI/res/values/strings.xml @@ -42,4 +42,6 @@ <string name="car_loading_profile">Loading</string> <!-- Message to inform user that the new user profile is loading with additional information on the previous and the next user. [CHAR LIMIT=100] --> <string name="car_loading_profile_developer_message">Loading user (from <xliff:g id="from_user" example="10">%1$d</xliff:g> to <xliff:g id="to_user" example="12">%2$d</xliff:g>)</string> + <!-- Text for the close button in Rear View Camera [CHAR LIMIT=30] --> + <string name="rear_view_camera_close_button_text">Close</string> </resources> diff --git a/packages/CarSystemUI/samples/sample3/rro/Android.bp b/packages/CarSystemUI/samples/sample3/rro/Android.bp new file mode 100644 index 000000000000..0eae7c271f66 --- /dev/null +++ b/packages/CarSystemUI/samples/sample3/rro/Android.bp @@ -0,0 +1,27 @@ +// +// Copyright (C) 2020 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +android_app { + name: "CarSystemUISampleThreeRRO", + resource_dirs: ["res"], + certificate: "platform", + platform_apis: true, + manifest: "AndroidManifest.xml", + aaptflags: [ + "--no-resource-deduping", + "--no-resource-removal", + ] +}
\ No newline at end of file diff --git a/packages/CarSystemUI/samples/sample3/rro/AndroidManifest.xml b/packages/CarSystemUI/samples/sample3/rro/AndroidManifest.xml new file mode 100644 index 000000000000..5c25056f7915 --- /dev/null +++ b/packages/CarSystemUI/samples/sample3/rro/AndroidManifest.xml @@ -0,0 +1,24 @@ +<!-- + ~ Copyright (C) 2020 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.systemui.rro"> + <overlay + android:targetPackage="com.android.systemui" + android:isStatic="false" + android:resourcesMap="@xml/car_sysui_overlays" + /> +</manifest>
\ No newline at end of file diff --git a/packages/CarSystemUI/samples/sample3/rro/res/drawable/car_ic_apps.xml b/packages/CarSystemUI/samples/sample3/rro/res/drawable/car_ic_apps.xml new file mode 100644 index 000000000000..a8d8a2f241f6 --- /dev/null +++ b/packages/CarSystemUI/samples/sample3/rro/res/drawable/car_ic_apps.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:viewportWidth="44" + android:viewportHeight="44" + android:width="44dp" + android:height="44dp"> +<path + android:pathData="M7.33333333 14.6666667L14.6666667 14.6666667L14.6666667 7.33333333L7.33333333 7.33333333L7.33333333 14.6666667ZM18.3333333 36.6666667L25.6666667 36.6666667L25.6666667 29.3333333L18.3333333 29.3333333L18.3333333 36.6666667ZM7.33333333 36.6666667L14.6666667 36.6666667L14.6666667 29.3333333L7.33333333 29.3333333L7.33333333 36.6666667ZM7.33333333 25.6666667L14.6666667 25.6666667L14.6666667 18.3333333L7.33333333 18.3333333L7.33333333 25.6666667ZM18.3333333 25.6666667L25.6666667 25.6666667L25.6666667 18.3333333L18.3333333 18.3333333L18.3333333 25.6666667ZM29.3333333 7.33333333L29.3333333 14.6666667L36.6666667 14.6666667L36.6666667 7.33333333L29.3333333 7.33333333ZM18.3333333 14.6666667L25.6666667 14.6666667L25.6666667 7.33333333L18.3333333 7.33333333L18.3333333 14.6666667ZM29.3333333 25.6666667L36.6666667 25.6666667L36.6666667 18.3333333L29.3333333 18.3333333L29.3333333 25.6666667ZM29.3333333 36.6666667L36.6666667 36.6666667L36.6666667 29.3333333L29.3333333 29.3333333L29.3333333 36.6666667Z" + android:fillColor="@color/car_nav_icon_fill_color" /> +</vector>
\ No newline at end of file diff --git a/packages/CarSystemUI/samples/sample3/rro/res/drawable/car_ic_home.xml b/packages/CarSystemUI/samples/sample3/rro/res/drawable/car_ic_home.xml new file mode 100644 index 000000000000..c78f0edd5594 --- /dev/null +++ b/packages/CarSystemUI/samples/sample3/rro/res/drawable/car_ic_home.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2020 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="@dimen/system_bar_icon_drawing_size" + android:height="@dimen/system_bar_icon_drawing_size" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M10,20v-6h4v6h5v-8h3L12,3 2,12h3v8z" + android:fillColor="@color/car_nav_icon_fill_color" /> +</vector>
\ No newline at end of file diff --git a/packages/CarSystemUI/samples/sample3/rro/res/drawable/car_ic_hvac.xml b/packages/CarSystemUI/samples/sample3/rro/res/drawable/car_ic_hvac.xml new file mode 100644 index 000000000000..55c968eacc4d --- /dev/null +++ b/packages/CarSystemUI/samples/sample3/rro/res/drawable/car_ic_hvac.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2018 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="@dimen/system_bar_icon_drawing_size" + android:height="@dimen/system_bar_icon_drawing_size" + android:viewportWidth="24" + android:viewportHeight="24"> + <path + android:pathData="M16.34,8.36l-2.29,0.82c-0.18,-0.13 -0.38,-0.25 -0.58,-0.34c0.17,-0.83 0.63,-1.58 1.36,-2.06C16.85,5.44 16.18,2 13.39,2C9,2 7.16,5.01 8.36,7.66l0.82,2.29c-0.13,0.18 -0.25,0.38 -0.34,0.58c-0.83,-0.17 -1.58,-0.63 -2.06,-1.36C5.44,7.15 2,7.82 2,10.61c0,4.4 3.01,6.24 5.66,5.03l2.29,-0.82c0.18,0.13 0.38,0.25 0.58,0.34c-0.17,0.83 -0.63,1.58 -1.36,2.06C7.15,18.56 7.82,22 10.61,22c4.4,0 6.24,-3.01 5.03,-5.66l-0.82,-2.29c0.13,-0.18 0.25,-0.38 0.34,-0.58c0.83,0.17 1.58,0.63 2.06,1.36c1.34,2.01 4.77,1.34 4.77,-1.45C22,9 18.99,7.16 16.34,8.36zM12,13.5c-0.83,0 -1.5,-0.67 -1.5,-1.5c0,-0.83 0.67,-1.5 1.5,-1.5c0.83,0 1.5,0.67 1.5,1.5C13.5,12.83 12.83,13.5 12,13.5zM10.24,5.22C10.74,4.44 11.89,4 13.39,4c0.79,0 0.71,0.86 0.34,1.11c-1.22,0.81 -2,2.06 -2.25,3.44c-0.21,0.03 -0.42,0.08 -0.62,0.15l-0.68,-1.88C10,6.42 9.86,5.81 10.24,5.22zM6.83,13.82c-0.4,0.18 -1.01,0.32 -1.61,-0.06C4.44,13.26 4,12.11 4,10.61c0,-0.79 0.86,-0.71 1.11,-0.34c0.81,1.22 2.06,2 3.44,2.25c0.03,0.21 0.08,0.42 0.15,0.62L6.83,13.82zM13.76,18.78c-0.5,0.77 -1.65,1.22 -3.15,1.22c-0.79,0 -0.71,-0.86 -0.34,-1.11c1.22,-0.81 2,-2.06 2.25,-3.44c0.21,-0.03 0.42,-0.08 0.62,-0.15l0.68,1.88C14,17.58 14.14,18.18 13.76,18.78zM18.89,13.73c-0.81,-1.22 -2.06,-2 -3.44,-2.25c-0.03,-0.21 -0.08,-0.42 -0.15,-0.62l1.88,-0.68c0.4,-0.18 1.01,-0.32 1.61,0.06c0.77,0.5 1.22,1.65 1.22,3.15C20,14.19 19.14,14.11 18.89,13.73z" + android:fillColor="@color/car_nav_icon_fill_color" /> +</vector> diff --git a/packages/CarSystemUI/samples/sample3/rro/res/drawable/car_ic_music.xml b/packages/CarSystemUI/samples/sample3/rro/res/drawable/car_ic_music.xml new file mode 100644 index 000000000000..6339ebb3ea8d --- /dev/null +++ b/packages/CarSystemUI/samples/sample3/rro/res/drawable/car_ic_music.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:viewportWidth="44" + android:viewportHeight="44" + android:width="44dp" + android:height="44dp"> + <path + android:pathData="M22 5.5L22 24.8416667C20.9183333 24.2183333 19.6716667 23.8333333 18.3333333 23.8333333C14.2816667 23.8333333 11 27.115 11 31.1666667C11 35.2183333 14.2816667 38.5 18.3333333 38.5C22.385 38.5 25.6666667 35.2183333 25.6666667 31.1666667L25.6666667 12.8333333L33 12.8333333L33 5.5L22 5.5Z" + android:fillColor="@color/car_nav_icon_fill_color" /> +</vector>
\ No newline at end of file diff --git a/packages/CarSystemUI/samples/sample3/rro/res/drawable/car_ic_navigation.xml b/packages/CarSystemUI/samples/sample3/rro/res/drawable/car_ic_navigation.xml new file mode 100644 index 000000000000..e1fabe07cdeb --- /dev/null +++ b/packages/CarSystemUI/samples/sample3/rro/res/drawable/car_ic_navigation.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:viewportWidth="44" + android:viewportHeight="44" + android:width="44dp" + android:height="44dp"> + <path + android:pathData="M39.8016667 20.6983333L23.3016667 4.19833333C22.5866667 3.48333333 21.4316667 3.48333333 20.7166667 4.19833333L4.21666667 20.6983333C3.50166667 21.4133333 3.50166667 22.5683333 4.21666667 23.2833333L20.7166667 39.7833333C21.4316667 40.4983333 22.5866667 40.4983333 23.3016667 39.7833333L39.8016667 23.2833333C40.5166667 22.5866667 40.5166667 21.4316667 39.8016667 20.6983333ZM25.6666667 26.5833333L25.6666667 22L18.3333333 22L18.3333333 27.5L14.6666667 27.5L14.6666667 20.1666667C14.6666667 19.1583333 15.4916667 18.3333333 16.5 18.3333333L25.6666667 18.3333333L25.6666667 13.75L32.0833333 20.1666667L25.6666667 26.5833333Z" + android:fillColor="@color/car_nav_icon_fill_color" /> +</vector>
\ No newline at end of file diff --git a/packages/CarSystemUI/samples/sample3/rro/res/drawable/car_ic_notification.xml b/packages/CarSystemUI/samples/sample3/rro/res/drawable/car_ic_notification.xml new file mode 100644 index 000000000000..aabf9161c11f --- /dev/null +++ b/packages/CarSystemUI/samples/sample3/rro/res/drawable/car_ic_notification.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="@dimen/system_bar_icon_drawing_size" + android:height="@dimen/system_bar_icon_drawing_size" + android:viewportWidth="44" + android:viewportHeight="44"> + <path + android:pathData="M22 39.125C23.925 39.125 25.5 37.55 25.5 35.625L18.5 35.625C18.5 37.55 20.0575 39.125 22 39.125ZM32.5 28.625L32.5 19.875C32.5 14.5025 29.63 10.005 24.625 8.815L24.625 7.625C24.625 6.1725 23.4525 5 22 5C20.5475 5 19.375 6.1725 19.375 7.625L19.375 8.815C14.3525 10.005 11.5 14.485 11.5 19.875L11.5 28.625L8 32.125L8 33.875L36 33.875L36 32.125L32.5 28.625Z" + android:fillColor="@color/car_nav_icon_fill_color" /> +</vector>
\ No newline at end of file diff --git a/packages/CarSystemUI/samples/sample3/rro/res/drawable/car_ic_overview.xml b/packages/CarSystemUI/samples/sample3/rro/res/drawable/car_ic_overview.xml new file mode 100644 index 000000000000..f185eb9afb75 --- /dev/null +++ b/packages/CarSystemUI/samples/sample3/rro/res/drawable/car_ic_overview.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:viewportWidth="44" + android:viewportHeight="44" + android:width="44dp" + android:height="44dp"> + <path + android:pathData="M36.92857 22.39286A14.53571 14.53571 0 0 1 7.857143 22.39286A14.53571 14.53571 0 0 1 36.92857 22.39286Z" + android:strokeColor="@color/car_nav_icon_fill_color" + android:strokeWidth="4" /> +</vector>
\ No newline at end of file diff --git a/packages/CarSystemUI/samples/sample3/rro/res/drawable/car_ic_phone.xml b/packages/CarSystemUI/samples/sample3/rro/res/drawable/car_ic_phone.xml new file mode 100644 index 000000000000..50e36b5a6e3c --- /dev/null +++ b/packages/CarSystemUI/samples/sample3/rro/res/drawable/car_ic_phone.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:viewportWidth="44" + android:viewportHeight="44" + android:width="44dp" + android:height="44dp"> + <path + android:pathData="M12.1366667 19.7816667C14.7766667 24.97 19.03 29.205 24.2183333 31.8633333L28.2516667 27.83C28.7466667 27.335 29.48 27.17 30.1216667 27.39C32.175 28.0683333 34.3933333 28.435 36.6666667 28.435C37.675 28.435 38.5 29.26 38.5 30.2683333L38.5 36.6666667C38.5 37.675 37.675 38.5 36.6666667 38.5C19.4516667 38.5 5.5 24.5483333 5.5 7.33333333C5.5 6.325 6.325 5.5 7.33333333 5.5L13.75 5.5C14.7583333 5.5 15.5833333 6.325 15.5833333 7.33333333C15.5833333 9.625 15.95 11.825 16.6283333 13.8783333C16.83 14.52 16.6833333 15.235 16.17 15.7483333L12.1366667 19.7816667Z" + android:fillColor="@color/car_nav_icon_fill_color" /> +</vector>
\ No newline at end of file diff --git a/packages/CarSystemUI/samples/sample3/rro/res/drawable/system_bar_background.xml b/packages/CarSystemUI/samples/sample3/rro/res/drawable/system_bar_background.xml new file mode 100644 index 000000000000..66da21ca23f8 --- /dev/null +++ b/packages/CarSystemUI/samples/sample3/rro/res/drawable/system_bar_background.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2020 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle" > + <solid + android:color="#404040" + /> +</shape>
\ No newline at end of file diff --git a/packages/CarSystemUI/samples/sample3/rro/res/layout/car_left_navigation_bar.xml b/packages/CarSystemUI/samples/sample3/rro/res/layout/car_left_navigation_bar.xml new file mode 100644 index 000000000000..3d1cd085e578 --- /dev/null +++ b/packages/CarSystemUI/samples/sample3/rro/res/layout/car_left_navigation_bar.xml @@ -0,0 +1,51 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +** +** 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. +*/ +--> + +<com.android.systemui.car.navigationbar.CarNavigationBarView + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:systemui="http://schemas.android.com/apk/res-auto" + android:layout_height="match_parent" + android:layout_width="match_parent" + android:orientation="vertical" + android:background="@android:color/transparent"> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_weight="1" + android:gravity="bottom" + android:orientation="vertical"> + + <com.android.systemui.statusbar.policy.Clock + android:id="@+id/clock" + android:textAppearance="@style/TextAppearance.StatusBar.Clock" + android:layout_height="wrap_content" + android:layout_width="match_parent" + android:singleLine="true" + android:gravity="center_horizontal" + android:paddingBottom="20dp" + /> + + <Space + android:layout_height="50dp" + android:layout_width="match_parent"/> + + </LinearLayout> + +</com.android.systemui.car.navigationbar.CarNavigationBarView> diff --git a/packages/CarSystemUI/samples/sample3/rro/res/layout/car_navigation_bar.xml b/packages/CarSystemUI/samples/sample3/rro/res/layout/car_navigation_bar.xml new file mode 100644 index 000000000000..8314ba5600a9 --- /dev/null +++ b/packages/CarSystemUI/samples/sample3/rro/res/layout/car_navigation_bar.xml @@ -0,0 +1,122 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2020 The Android Open Source Project. + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<com.android.systemui.car.navigationbar.CarNavigationBarView + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:systemui="http://schemas.android.com/apk/res-auto" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:background="@drawable/system_bar_background" + android:gravity="center" + android:orientation="horizontal"> + + <RelativeLayout + android:id="@+id/nav_buttons" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layoutDirection="ltr"> + + <com.android.systemui.car.hvac.AdjustableTemperatureView + android:id="@+id/driver_hvac" + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:gravity="center_vertical" + systemui:hvacAreaId="49" + systemui:hvacTempFormat="%.0f\u00B0" /> + + <LinearLayout + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:layout_centerInParent="true" + android:layout_weight="1" + android:gravity="center" + android:layoutDirection="ltr" + android:paddingEnd="@dimen/system_bar_button_group_padding" + android:paddingStart="@dimen/system_bar_button_group_padding"> + + <Space + android:layout_width="0dp" + android:layout_height="match_parent" + android:layout_weight="1"/> + + <com.android.systemui.car.navigationbar.CarNavigationButton + android:id="@+id/home" + style="@style/NavigationBarButton" + systemui:componentNames="com.android.car.carlauncher/.CarLauncher" + systemui:highlightWhenSelected="true" + systemui:icon="@drawable/car_ic_home" + systemui:intent="intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.HOME;launchFlags=0x14000000;end"/> + + <com.android.systemui.car.navigationbar.CarNavigationButton + android:id="@+id/phone_nav" + style="@style/NavigationBarButton" + systemui:highlightWhenSelected="true" + systemui:icon="@drawable/car_ic_phone" + systemui:intent="intent:#Intent;action=android.intent.action.MAIN;category=android.intent.category.LAUNCHER;package=com.android.car.dialer;launchFlags=0x10000000;end" + systemui:packages="com.android.car.dialer"/> + + <com.android.systemui.car.navigationbar.CarNavigationButton + android:id="@+id/grid_nav" + style="@style/NavigationBarButton" + systemui:componentNames="com.android.car.carlauncher/.AppGridActivity" + systemui:highlightWhenSelected="true" + systemui:icon="@drawable/car_ic_apps" + systemui:intent="intent:#Intent;component=com.android.car.carlauncher/.AppGridActivity;launchFlags=0x24000000;end"/> + + <com.android.systemui.car.navigationbar.CarNavigationButton + android:id="@+id/hvac" + style="@style/NavigationBarButton" + systemui:highlightWhenSelected="true" + systemui:icon="@drawable/car_ic_hvac" + systemui:intent="intent:#Intent;action=android.car.intent.action.TOGGLE_HVAC_CONTROLS;end" + systemui:broadcast="true"/> + + <com.android.systemui.car.navigationbar.CarNavigationButton + android:id="@+id/notifications" + style="@style/NavigationBarButton" + systemui:highlightWhenSelected="true" + systemui:icon="@drawable/car_ic_notification" + systemui:longIntent="intent:#Intent;component=com.android.car.bugreport/.BugReportActivity;end"/> + + <Space + android:layout_width="0dp" + android:layout_height="match_parent" + android:layout_weight="1"/> + </LinearLayout> + + <com.android.systemui.car.hvac.AdjustableTemperatureView + android:id="@+id/passenger_hvac" + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:layout_alignParentEnd="true" + android:gravity="center_vertical" + systemui:hvacAreaId="68" + systemui:hvacTempFormat="%.0f\u00B0" /> + </RelativeLayout> + + <LinearLayout + android:id="@+id/lock_screen_nav_buttons" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_weight="1" + android:gravity="center" + android:layoutDirection="ltr" + android:paddingEnd="@dimen/car_keyline_1" + android:paddingStart="@dimen/car_keyline_1" + android:visibility="gone" + /> +</com.android.systemui.car.navigationbar.CarNavigationBarView>
\ No newline at end of file diff --git a/packages/CarSystemUI/samples/sample3/rro/res/values/attrs.xml b/packages/CarSystemUI/samples/sample3/rro/res/values/attrs.xml new file mode 100644 index 000000000000..bc7ded20031a --- /dev/null +++ b/packages/CarSystemUI/samples/sample3/rro/res/values/attrs.xml @@ -0,0 +1,43 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2020 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<resources> + <attr name="broadcast" format="boolean"/> + <attr name="icon" format="reference"/> + <attr name="intent" format="string"/> + <attr name="longIntent" format="string"/> + <attr name="componentNames" format="string" /> + <attr name="highlightWhenSelected" format="boolean" /> + <attr name="categories" format="string"/> + <attr name="packages" format="string" /> + + <!-- Custom attributes to configure hvac values --> + <declare-styleable name="AnimatedTemperatureView"> + <attr name="hvacAreaId" format="integer"/> + <attr name="hvacPropertyId" format="integer"/> + <attr name="hvacTempFormat" format="string"/> + <!-- how far away the animations should center around --> + <attr name="hvacPivotOffset" format="dimension"/> + <attr name="hvacMinValue" format="float"/> + <attr name="hvacMaxValue" format="float"/> + <attr name="hvacMinText" format="string|reference"/> + <attr name="hvacMaxText" format="string|reference"/> + <attr name="android:gravity"/> + <attr name="android:minEms"/> + <attr name="android:textAppearance"/> + </declare-styleable> +</resources> diff --git a/packages/CarSystemUI/samples/sample3/rro/res/values/colors.xml b/packages/CarSystemUI/samples/sample3/rro/res/values/colors.xml new file mode 100644 index 000000000000..f98cb96e76e8 --- /dev/null +++ b/packages/CarSystemUI/samples/sample3/rro/res/values/colors.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2020 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<resources xmlns:android="http://schemas.android.com/apk/res/android"> + <color name="car_nav_icon_fill_color">#8F8F8F</color> +</resources> diff --git a/packages/CarSystemUI/samples/sample3/rro/res/values/config.xml b/packages/CarSystemUI/samples/sample3/rro/res/values/config.xml new file mode 100644 index 000000000000..2148e7cbfd96 --- /dev/null +++ b/packages/CarSystemUI/samples/sample3/rro/res/values/config.xml @@ -0,0 +1,57 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2020 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<resources> + <!-- Configure which system bars should be displayed. --> + <bool name="config_enableTopNavigationBar">false</bool> + <bool name="config_enableLeftNavigationBar">true</bool> + <bool name="config_enableRightNavigationBar">false</bool> + <bool name="config_enableBottomNavigationBar">true</bool> + + <!-- Configure the type of each system bar. Each system bar must have a unique type. --> + <!-- STATUS_BAR = 0--> + <!-- NAVIGATION_BAR = 1--> + <!-- STATUS_BAR_EXTRA = 2--> + <!-- NAVIGATION_BAR_EXTRA = 3--> + <integer name="config_topSystemBarType">2</integer> + <integer name="config_leftSystemBarType">0</integer> + <integer name="config_rightSystemBarType">3</integer> + <integer name="config_bottomSystemBarType">1</integer> + + <!-- Configure the relative z-order among the system bars. When two system bars overlap (e.g. + if both top bar and left bar are enabled, it creates an overlapping space in the upper left + corner), the system bar with the higher z-order takes the overlapping space and padding is + applied to the other bar.--> + <!-- NOTE: If two overlapping system bars have the same z-order, SystemBarConfigs will throw a + RuntimeException, since their placing order cannot be determined. Bars that do not overlap + are allowed to have the same z-order. --> + <!-- NOTE: If the z-order of a bar is 10 or above, it will also appear on top of HUN's. --> + <integer name="config_topSystemBarZOrder">0</integer> + <integer name="config_leftSystemBarZOrder">10</integer> + <integer name="config_rightSystemBarZOrder">0</integer> + <integer name="config_bottomSystemBarZOrder">15</integer> + + <!-- Whether heads-up notifications should be shown on the bottom. If false, heads-up + notifications will be shown pushed to the top of their parent container. If true, they will + be shown pushed to the bottom of their parent container. If true, then should override + config_headsUpNotificationAnimationHelper to use a different AnimationHelper, such as + com.android.car.notification.headsup.animationhelper. + CarHeadsUpNotificationBottomAnimationHelper. --> + <bool name="config_showHeadsUpNotificationOnBottom">false</bool> + + <string name="config_notificationPanelViewMediator" translatable="false">com.android.systemui.car.notification.BottomNotificationPanelViewMediator</string> +</resources>
\ No newline at end of file diff --git a/packages/CarSystemUI/samples/sample3/rro/res/values/dimens.xml b/packages/CarSystemUI/samples/sample3/rro/res/values/dimens.xml new file mode 100644 index 000000000000..c89f94938582 --- /dev/null +++ b/packages/CarSystemUI/samples/sample3/rro/res/values/dimens.xml @@ -0,0 +1,22 @@ +<?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 + --> +<resources> + <dimen name="car_left_navigation_bar_width">280dp</dimen> + <dimen name="car_keyline_1">24dp</dimen> + <dimen name="system_bar_button_group_padding">64dp</dimen> + <dimen name="system_bar_icon_drawing_size">44dp</dimen> +</resources> diff --git a/packages/CarSystemUI/samples/sample3/rro/res/values/styles.xml b/packages/CarSystemUI/samples/sample3/rro/res/values/styles.xml new file mode 100644 index 000000000000..bad36917d2cf --- /dev/null +++ b/packages/CarSystemUI/samples/sample3/rro/res/values/styles.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2020 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<resources xmlns:android="http://schemas.android.com/apk/res/android"> + <style name="TextAppearance.StatusBar.Clock" + parent="@*android:style/TextAppearance.StatusBar.Icon"> + <item name="android:textSize">40sp</item> + <item name="android:fontFamily">sans-serif-regular</item> + <item name="android:textColor">#FFFFFF</item> + </style> + + <style name="NavigationBarButton"> + <item name="android:layout_height">96dp</item> + <item name="android:layout_width">96dp</item> + <item name="android:background">?android:attr/selectableItemBackground</item> + </style> +</resources>
\ No newline at end of file diff --git a/packages/CarSystemUI/samples/sample3/rro/res/xml/car_sysui_overlays.xml b/packages/CarSystemUI/samples/sample3/rro/res/xml/car_sysui_overlays.xml new file mode 100644 index 000000000000..f08d9684f2d5 --- /dev/null +++ b/packages/CarSystemUI/samples/sample3/rro/res/xml/car_sysui_overlays.xml @@ -0,0 +1,75 @@ + +<!-- + ~ Copyright (C) 2020 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<overlay> + <item target="layout/car_navigation_bar" value="@layout/car_navigation_bar"/> + <item target="layout/car_left_navigation_bar" value="@layout/car_left_navigation_bar"/> + + <item target="bool/config_enableTopNavigationBar" value="@bool/config_enableTopNavigationBar"/> + <item target="bool/config_enableLeftNavigationBar" value="@bool/config_enableLeftNavigationBar"/> + <item target="bool/config_enableRightNavigationBar" value="@bool/config_enableRightNavigationBar"/> + <item target="bool/config_enableBottomNavigationBar" value="@bool/config_enableBottomNavigationBar"/> + <item target="bool/config_showHeadsUpNotificationOnBottom" value="@bool/config_showHeadsUpNotificationOnBottom"/> + + <item target="attr/icon" value="@attr/icon"/> + <item target="attr/intent" value="@attr/intent"/> + <item target="attr/longIntent" value="@attr/longIntent"/> + <item target="attr/componentNames" value="@attr/componentNames"/> + <item target="attr/highlightWhenSelected" value="@attr/highlightWhenSelected"/> + <item target="attr/categories" value="@attr/categories"/> + <item target="attr/packages" value="@attr/packages"/> + <item target="attr/hvacAreaId" value="@attr/hvacAreaId"/> + <item target="attr/hvacPropertyId" value="@attr/hvacPropertyId"/> + <item target="attr/hvacTempFormat" value="@attr/hvacTempFormat"/> + <item target="attr/hvacPivotOffset" value="@attr/hvacPivotOffset"/> + <item target="attr/hvacMinValue" value="@attr/hvacMinValue"/> + <item target="attr/hvacMaxValue" value="@attr/hvacMaxValue"/> + <item target="attr/hvacMinText" value="@attr/hvacMinText"/> + <item target="attr/hvacMaxText" value="@attr/hvacMaxText"/> + <!-- start the intent as a broad cast instead of an activity if true--> + <item target="attr/broadcast" value="@attr/broadcast"/> + + <item target="color/car_nav_icon_fill_color" value="@color/car_nav_icon_fill_color" /> + + <item target="drawable/car_ic_overview" value="@drawable/car_ic_overview" /> + <item target="drawable/car_ic_home" value="@drawable/car_ic_home" /> + <item target="drawable/car_ic_hvac" value="@drawable/car_ic_hvac" /> + <item target="drawable/car_ic_apps" value="@drawable/car_ic_apps" /> + <item target="drawable/car_ic_music" value="@drawable/car_ic_music" /> + <item target="drawable/car_ic_notification" value="@drawable/car_ic_notification" /> + <item target="drawable/car_ic_phone" value="@drawable/car_ic_phone" /> + <item target="drawable/car_ic_navigation" value="@drawable/car_ic_navigation" /> + + <item target="dimen/car_left_navigation_bar_width" value="@dimen/car_left_navigation_bar_width" /> + <item target="dimen/car_keyline_1" value="@dimen/car_keyline_1" /> + <item target="dimen/system_bar_button_group_padding" value="@dimen/system_bar_button_group_padding" /> + <item target="dimen/system_bar_icon_drawing_size" value="@dimen/system_bar_icon_drawing_size" /> + + <item target="integer/config_topSystemBarType" value="@integer/config_topSystemBarType"/> + <item target="integer/config_leftSystemBarType" value="@integer/config_leftSystemBarType"/> + <item target="integer/config_rightSystemBarType" value="@integer/config_rightSystemBarType"/> + <item target="integer/config_bottomSystemBarType" value="@integer/config_bottomSystemBarType"/> + + <item target="integer/config_topSystemBarZOrder" value="@integer/config_topSystemBarZOrder"/> + <item target="integer/config_leftSystemBarZOrder" value="@integer/config_leftSystemBarZOrder"/> + <item target="integer/config_rightSystemBarZOrder" value="@integer/config_rightSystemBarZOrder"/> + <item target="integer/config_bottomSystemBarZOrder" value="@integer/config_bottomSystemBarZOrder"/> + + <item target="string/config_notificationPanelViewMediator" value="@string/config_notificationPanelViewMediator"/> + + <item target="style/NavigationBarButton" value="@style/NavigationBarButton"/> +</overlay>
\ No newline at end of file diff --git a/packages/CarSystemUI/src/com/android/systemui/CarGlobalRootComponent.java b/packages/CarSystemUI/src/com/android/systemui/CarGlobalRootComponent.java index b17ad0febb90..b056dcf8fd2b 100644 --- a/packages/CarSystemUI/src/com/android/systemui/CarGlobalRootComponent.java +++ b/packages/CarSystemUI/src/com/android/systemui/CarGlobalRootComponent.java @@ -19,6 +19,7 @@ package com.android.systemui; import com.android.systemui.dagger.GlobalModule; import com.android.systemui.dagger.GlobalRootComponent; import com.android.systemui.dagger.WMModule; +import com.android.systemui.wmshell.CarWMComponent; import javax.inject.Singleton; @@ -41,6 +42,12 @@ public interface CarGlobalRootComponent extends GlobalRootComponent { CarGlobalRootComponent build(); } + /** + * Builder for a WMComponent. + */ + @Override + CarWMComponent.Builder getWMComponentBuilder(); + @Override CarSysUIComponent.Builder getSysUIComponent(); } diff --git a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java index 51fda965dcd0..1d35bbb84464 100644 --- a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java +++ b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIModule.java @@ -64,7 +64,6 @@ import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.DeviceProvisionedController; import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.volume.VolumeDialogComponent; -import com.android.systemui.wmshell.CarWMShellModule; import javax.inject.Named; @@ -74,8 +73,7 @@ import dagger.Provides; @Module( includes = { - QSModule.class, - CarWMShellModule.class + QSModule.class }) abstract class CarSystemUIModule { diff --git a/packages/CarSystemUI/src/com/android/systemui/car/rvc/RearViewCameraViewController.java b/packages/CarSystemUI/src/com/android/systemui/car/rvc/RearViewCameraViewController.java new file mode 100644 index 000000000000..d63463309ec5 --- /dev/null +++ b/packages/CarSystemUI/src/com/android/systemui/car/rvc/RearViewCameraViewController.java @@ -0,0 +1,141 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.car.rvc; + +import android.app.ActivityView; +import android.app.ActivityView.StateCallback; +import android.content.ComponentName; +import android.content.Intent; +import android.content.res.Resources; +import android.util.Slog; +import android.view.ViewGroup; +import android.widget.LinearLayout.LayoutParams; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.systemui.R; +import com.android.systemui.car.window.OverlayViewController; +import com.android.systemui.car.window.OverlayViewGlobalStateController; +import com.android.systemui.dagger.SysUISingleton; +import com.android.systemui.dagger.qualifiers.Main; + +import javax.inject.Inject; + +/** View controller for the rear view camera. */ +@SysUISingleton +public class RearViewCameraViewController extends OverlayViewController { + private static final String TAG = "RearViewCameraView"; + private static final boolean DBG = false; + + private final ComponentName mRearViewCameraActivity; + private ViewGroup mRvcView; + private final LayoutParams mRvcViewLayoutParams = new LayoutParams( + LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, /* weight= */ 1.0f); + @VisibleForTesting + ActivityView mActivityView; + @VisibleForTesting + final StateCallback mActivityViewCallback = new StateCallback() { + @Override + public void onActivityViewReady(ActivityView view) { + Intent intent = new Intent(Intent.ACTION_MAIN) + .setComponent(mRearViewCameraActivity) + .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + .addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT) + .addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK); + // TODO(b/170899079): Migrate this to FixedActivityService. + view.startActivity(intent); + } + + @Override + public void onActivityViewDestroyed(ActivityView view) {} + }; + + @Inject + public RearViewCameraViewController( + @Main Resources resources, + OverlayViewGlobalStateController overlayViewGlobalStateController) { + super(R.id.rear_view_camera_stub, overlayViewGlobalStateController); + String rearViewCameraActivityName = resources.getString( + R.string.config_rearViewCameraActivity); + if (!rearViewCameraActivityName.isEmpty()) { + mRearViewCameraActivity = ComponentName.unflattenFromString(rearViewCameraActivityName); + if (DBG) Slog.d(TAG, "mRearViewCameraActivity=" + mRearViewCameraActivity); + } else { + mRearViewCameraActivity = null; + Slog.e(TAG, "RearViewCameraViewController is disabled, since no Activity is defined"); + } + } + + @Override + protected void onFinishInflate() { + mRvcView = (ViewGroup) getLayout().findViewById(R.id.rear_view_camera_container); + getLayout().findViewById(R.id.close_button).setOnClickListener(v -> { + stop(); + }); + } + + @Override + protected void hideInternal() { + super.hideInternal(); + if (DBG) Slog.d(TAG, "hideInternal: mActivityView=" + mActivityView); + if (mActivityView == null) return; + mRvcView.removeView(mActivityView); + // Release ActivityView since the Activity on ActivityView (with showWhenLocked flag) keeps + // running even if ActivityView is hidden. + mActivityView.release(); + mActivityView = null; + } + + @Override + protected void showInternal() { + super.showInternal(); + if (DBG) Slog.d(TAG, "showInternal: mActivityView=" + mActivityView); + if (mActivityView != null) return; + mActivityView = new ActivityView(mRvcView.getContext()); + mActivityView.setCallback(mActivityViewCallback); + mActivityView.setLayoutParams(mRvcViewLayoutParams); + mRvcView.addView(mActivityView, /* index= */ 0); + } + + boolean isShown() { + return mActivityView != null; + } + + boolean isEnabled() { + return mRearViewCameraActivity != null; + } + + @Override + protected boolean shouldShowHUN() { + return false; + } + + @Override + protected boolean shouldShowWhenOccluded() { + // Returns true to show it on top of Keylock. + return true; + } + + @Override + protected boolean shouldShowNavigationBarInsets() { + return true; + } + + @Override + protected boolean shouldShowStatusBarInsets() { + return true; + } +} diff --git a/packages/CarSystemUI/src/com/android/systemui/car/rvc/RearViewCameraViewMediator.java b/packages/CarSystemUI/src/com/android/systemui/car/rvc/RearViewCameraViewMediator.java new file mode 100644 index 000000000000..c575c423b256 --- /dev/null +++ b/packages/CarSystemUI/src/com/android/systemui/car/rvc/RearViewCameraViewMediator.java @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.car.rvc; + +import android.car.Car; +import android.car.VehicleGear; +import android.car.VehiclePropertyIds; +import android.car.hardware.CarPropertyValue; +import android.car.hardware.property.CarPropertyManager; +import android.car.hardware.property.CarPropertyManager.CarPropertyEventCallback; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.os.UserHandle; +import android.util.Slog; + +import com.android.systemui.broadcast.BroadcastDispatcher; +import com.android.systemui.car.CarServiceProvider; +import com.android.systemui.car.window.OverlayViewMediator; +import com.android.systemui.dagger.SysUISingleton; + +import javax.inject.Inject; + +/** + * View mediator for the rear view camera (RVC), which monitors the gear changes and shows + * the RVC when the gear position is R and otherwise it hides the RVC. + */ +@SysUISingleton +public class RearViewCameraViewMediator implements OverlayViewMediator { + private static final String TAG = "RearViewCameraView"; + private static final boolean DBG = false; + + private final RearViewCameraViewController mRearViewCameraViewController; + private final CarServiceProvider mCarServiceProvider; + private final BroadcastDispatcher mBroadcastDispatcher; + + private CarPropertyManager mCarPropertyManager; + // TODO(b/170792252): Replace the following with the callback from CarEvsManager if it's ready. + private final CarPropertyEventCallback mPropertyEventCallback = new CarPropertyEventCallback() { + @Override + public void onChangeEvent(CarPropertyValue value) { + if (DBG) Slog.d(TAG, "onChangeEvent value=" + value); + if (value.getPropertyId() != VehiclePropertyIds.GEAR_SELECTION) { + Slog.w(TAG, "Got the event for non-registered property: " + value.getPropertyId()); + return; + } + if ((Integer) value.getValue() == VehicleGear.GEAR_REVERSE) { + mRearViewCameraViewController.start(); + } else { + mRearViewCameraViewController.stop(); + } + } + @Override + public void onErrorEvent(int propId, int zone) { + Slog.e(TAG, "onErrorEvent propId=" + propId + ", zone=" + zone); + } + }; + + private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if (DBG) Slog.d(TAG, "onReceive: " + intent); + if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(intent.getAction()) + && mRearViewCameraViewController.isShown()) { + mRearViewCameraViewController.stop(); + } + } + }; + + @Inject + public RearViewCameraViewMediator( + RearViewCameraViewController rearViewCameraViewController, + CarServiceProvider carServiceProvider, + BroadcastDispatcher broadcastDispatcher) { + if (DBG) Slog.d(TAG, "RearViewCameraViewMediator:init"); + mRearViewCameraViewController = rearViewCameraViewController; + mCarServiceProvider = carServiceProvider; + mBroadcastDispatcher = broadcastDispatcher; + } + + @Override + public void registerListeners() { + if (DBG) Slog.d(TAG, "RearViewCameraViewMediator:registerListeners"); + if (!mRearViewCameraViewController.isEnabled()) { + Slog.i(TAG, "RearViewCameraViewController isn't enabled"); + return; + } + + mCarServiceProvider.addListener(car -> { + mCarPropertyManager = (CarPropertyManager) car.getCarManager(Car.PROPERTY_SERVICE); + if (mCarPropertyManager == null) { + Slog.e(TAG, "Unable to get CarPropertyManager"); + return; + } + if (DBG) Slog.d(TAG, "Registering mPropertyEventCallback."); + mCarPropertyManager.registerCallback(mPropertyEventCallback, + VehiclePropertyIds.GEAR_SELECTION, CarPropertyManager.SENSOR_RATE_UI); + }); + mBroadcastDispatcher.registerReceiver(mBroadcastReceiver, + new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS), /* executor= */ null, + UserHandle.ALL); + } + + @Override + public void setupOverlayContentViewControllers() {} +} diff --git a/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayWindowModule.java b/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayWindowModule.java index 5a16efa3dd9b..fcbb0b807e74 100644 --- a/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayWindowModule.java +++ b/packages/CarSystemUI/src/com/android/systemui/car/window/OverlayWindowModule.java @@ -20,6 +20,7 @@ import com.android.systemui.car.keyguard.CarKeyguardViewMediator; import com.android.systemui.car.notification.BottomNotificationPanelViewMediator; import com.android.systemui.car.notification.NotificationPanelViewMediator; import com.android.systemui.car.notification.TopNotificationPanelViewMediator; +import com.android.systemui.car.rvc.RearViewCameraViewMediator; import com.android.systemui.car.userswitcher.FullscreenUserSwitcherViewMediator; import com.android.systemui.car.userswitcher.UserSwitchTransitionViewMediator; @@ -75,4 +76,11 @@ public abstract class OverlayWindowModule { @ClassKey(UserSwitchTransitionViewMediator.class) public abstract OverlayViewMediator bindUserSwitchTransitionViewMediator( UserSwitchTransitionViewMediator userSwitchTransitionViewMediator); + + /** Injects RearViewCameraViewMediator. */ + @Binds + @IntoMap + @ClassKey(RearViewCameraViewMediator.class) + public abstract OverlayViewMediator bindRearViewCameraViewMediator( + RearViewCameraViewMediator overlayViewsMediator); } diff --git a/packages/CarSystemUI/src/com/android/systemui/wmshell/CarWMComponent.java b/packages/CarSystemUI/src/com/android/systemui/wmshell/CarWMComponent.java new file mode 100644 index 000000000000..c6a7fd2f822d --- /dev/null +++ b/packages/CarSystemUI/src/com/android/systemui/wmshell/CarWMComponent.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.wmshell; + +import com.android.systemui.dagger.WMComponent; +import com.android.systemui.dagger.WMSingleton; + +import dagger.Subcomponent; + + +/** + * Dagger Subcomponent for WindowManager. + */ +@WMSingleton +@Subcomponent(modules = {CarWMShellModule.class}) +public interface CarWMComponent extends WMComponent { + + /** + * Builder for a SysUIComponent. + */ + @Subcomponent.Builder + interface Builder extends WMComponent.Builder { + CarWMComponent build(); + } +} diff --git a/packages/CarSystemUI/src/com/android/systemui/wmshell/CarWMShellModule.java b/packages/CarSystemUI/src/com/android/systemui/wmshell/CarWMShellModule.java index 6d31a8d69ebe..27aabffd5090 100644 --- a/packages/CarSystemUI/src/com/android/systemui/wmshell/CarWMShellModule.java +++ b/packages/CarSystemUI/src/com/android/systemui/wmshell/CarWMShellModule.java @@ -20,25 +20,30 @@ import android.content.Context; import android.os.Handler; import android.view.IWindowManager; -import com.android.systemui.dagger.SysUISingleton; +import com.android.systemui.dagger.WMSingleton; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.wm.DisplaySystemBarsController; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.DisplayImeController; import com.android.wm.shell.common.TransactionPool; +import com.android.wm.shell.pip.Pip; +import dagger.BindsOptionalOf; import dagger.Module; import dagger.Provides; /** Provides dependencies from {@link com.android.wm.shell} for CarSystemUI. */ @Module(includes = WMShellBaseModule.class) -public class CarWMShellModule { - @SysUISingleton +public abstract class CarWMShellModule { + @WMSingleton @Provides - DisplayImeController provideDisplayImeController(Context context, + static DisplayImeController provideDisplayImeController(Context context, IWindowManager wmService, DisplayController displayController, @Main Handler mainHandler, TransactionPool transactionPool) { return new DisplaySystemBarsController(context, wmService, displayController, mainHandler, transactionPool); } + + @BindsOptionalOf + abstract Pip optionalPip(); } diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/rvc/RearViewCameraViewControllerTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/rvc/RearViewCameraViewControllerTest.java new file mode 100644 index 000000000000..a6160ecf1c62 --- /dev/null +++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/rvc/RearViewCameraViewControllerTest.java @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.car.rvc; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.verify; + +import android.app.ActivityView; +import android.content.ComponentName; +import android.content.Intent; +import android.testing.TestableLooper; +import android.view.LayoutInflater; +import android.view.ViewGroup; + +import androidx.test.filters.SmallTest; + +import com.android.systemui.R; +import com.android.systemui.SysuiTestCase; +import com.android.systemui.car.CarSystemUiTest; +import com.android.systemui.car.window.OverlayViewGlobalStateController; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; + +@CarSystemUiTest +@RunWith(MockitoJUnitRunner.class) +@TestableLooper.RunWithLooper +@SmallTest +public class RearViewCameraViewControllerTest extends SysuiTestCase { + private static final String TEST_ACTIVITY_NAME = "testPackage/testActivity"; + private RearViewCameraViewController mRearViewCameraViewController; + + @Mock + private OverlayViewGlobalStateController mOverlayViewGlobalStateController; + @Mock + private ActivityView mMockActivityView; + @Captor + private ArgumentCaptor<Intent> mIntentCaptor; + + private void setUpRearViewCameraViewController(String testActivityName) { + mContext.getOrCreateTestableResources().addOverride( + R.string.config_rearViewCameraActivity, testActivityName); + mRearViewCameraViewController = new RearViewCameraViewController( + mContext.getOrCreateTestableResources().getResources(), + mOverlayViewGlobalStateController); + mRearViewCameraViewController.inflate((ViewGroup) LayoutInflater.from(mContext).inflate( + R.layout.sysui_overlay_window, /* root= */ null)); + } + + @Test + public void testEmptyResourceDisablesController() { + setUpRearViewCameraViewController(""); + + assertThat(mRearViewCameraViewController.isEnabled()).isFalse(); + } + + @Test + public void testNonEmptyResourceEnablesController() { + setUpRearViewCameraViewController(TEST_ACTIVITY_NAME); + + assertThat(mRearViewCameraViewController.isEnabled()).isTrue(); + } + + @Test + public void testShowInternal() { + setUpRearViewCameraViewController(TEST_ACTIVITY_NAME); + assertThat(mRearViewCameraViewController.isShown()).isFalse(); + assertThat(mRearViewCameraViewController.mActivityView).isNull(); + + mRearViewCameraViewController.showInternal(); + + assertThat(mRearViewCameraViewController.isShown()).isTrue(); + assertThat(mRearViewCameraViewController.mActivityView).isNotNull(); + } + + @Test + public void testHideInternal() { + setUpRearViewCameraViewController(TEST_ACTIVITY_NAME); + assertThat(mRearViewCameraViewController.isShown()).isFalse(); + mRearViewCameraViewController.showInternal(); + assertThat(mRearViewCameraViewController.isShown()).isTrue(); + + mRearViewCameraViewController.hideInternal(); + + assertThat(mRearViewCameraViewController.isShown()).isFalse(); + assertThat(mRearViewCameraViewController.mActivityView).isNull(); + } + + @Test + public void testOnActivityViewReady_fireIntent() { + setUpRearViewCameraViewController(TEST_ACTIVITY_NAME); + mRearViewCameraViewController.mActivityViewCallback.onActivityViewReady(mMockActivityView); + + verify(mMockActivityView).startActivity(mIntentCaptor.capture()); + ComponentName expectedComponent = ComponentName.unflattenFromString(TEST_ACTIVITY_NAME); + assertThat(mIntentCaptor.getValue().getComponent()).isEqualTo(expectedComponent); + } +} diff --git a/packages/CarSystemUI/tests/src/com/android/systemui/car/rvc/RearViewCameraViewMediatorTest.java b/packages/CarSystemUI/tests/src/com/android/systemui/car/rvc/RearViewCameraViewMediatorTest.java new file mode 100644 index 000000000000..5be8f91ff430 --- /dev/null +++ b/packages/CarSystemUI/tests/src/com/android/systemui/car/rvc/RearViewCameraViewMediatorTest.java @@ -0,0 +1,169 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.car.rvc; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyFloat; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.car.Car; +import android.car.VehicleAreaType; +import android.car.VehicleGear; +import android.car.VehiclePropertyIds; +import android.car.hardware.CarPropertyValue; +import android.car.hardware.property.CarPropertyManager; +import android.car.hardware.property.CarPropertyManager.CarPropertyEventCallback; +import android.content.BroadcastReceiver; +import android.content.Intent; +import android.content.IntentFilter; +import android.testing.TestableLooper; +import android.util.Log; + +import androidx.test.filters.SmallTest; + +import com.android.systemui.SysuiTestCase; +import com.android.systemui.broadcast.BroadcastDispatcher; +import com.android.systemui.car.CarServiceProvider; +import com.android.systemui.car.CarServiceProvider.CarServiceOnConnectedListener; +import com.android.systemui.car.CarSystemUiTest; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; + +@CarSystemUiTest +@RunWith(MockitoJUnitRunner.class) +@TestableLooper.RunWithLooper +@SmallTest +public class RearViewCameraViewMediatorTest extends SysuiTestCase { + private static final String TAG = RearViewCameraViewMediatorTest.class.getSimpleName(); + + private RearViewCameraViewMediator mRearViewCameraViewMediator; + + @Mock + private CarServiceProvider mCarServiceProvider; + @Mock + private Car mCar; + @Mock + private CarPropertyManager mCarPropertyManager; + @Captor + private ArgumentCaptor<CarPropertyEventCallback> mCarPropertyEventCallbackCaptor; + + @Mock + private BroadcastDispatcher mBroadcastDispatcher; + @Captor + private ArgumentCaptor<BroadcastReceiver> mBroadcastReceiverCaptor; + @Captor + private ArgumentCaptor<IntentFilter> mIntentFilterCaptor; + + @Mock + private RearViewCameraViewController mRearViewCameraViewController; + + @Before + public void setUp() throws Exception { + mRearViewCameraViewMediator = new RearViewCameraViewMediator( + mRearViewCameraViewController, mCarServiceProvider, mBroadcastDispatcher); + } + + public void setUpListener() { + doAnswer(invocation -> { + CarServiceOnConnectedListener listener = invocation.getArgument(0); + listener.onConnected(mCar); + return null; + }).when(mCarServiceProvider).addListener(any(CarServiceOnConnectedListener.class)); + when(mCar.getCarManager(Car.PROPERTY_SERVICE)).thenReturn(mCarPropertyManager); + when(mRearViewCameraViewController.isEnabled()).thenReturn(true); + + mRearViewCameraViewMediator.registerListeners(); + + verify(mCarPropertyManager).registerCallback(mCarPropertyEventCallbackCaptor.capture(), + eq(VehiclePropertyIds.GEAR_SELECTION), anyFloat()); + verify(mBroadcastDispatcher).registerReceiver(mBroadcastReceiverCaptor.capture(), + mIntentFilterCaptor.capture(), any(), any()); + assertThat(mIntentFilterCaptor.getValue().getAction(0)).isEqualTo( + Intent.ACTION_CLOSE_SYSTEM_DIALOGS); + } + + @Test + public void testDoesnNotRegisterListenersWhenRearViewCameraViewControllerIsDisabled() { + when(mRearViewCameraViewController.isEnabled()).thenReturn(false); + + mRearViewCameraViewMediator.registerListeners(); + + verify(mCarPropertyManager, never()).registerCallback(any(), anyInt(), anyFloat()); + verify(mBroadcastDispatcher, never()).registerReceiver(any(), any(), any()); + } + + @Test + public void testGearReverseStartsRearViewCamera() { + setUpListener(); + + CarPropertyValue<Integer> gearReverse = new CarPropertyValue( + VehiclePropertyIds.GEAR_SELECTION, VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL, + VehicleGear.GEAR_REVERSE); + mCarPropertyEventCallbackCaptor.getValue().onChangeEvent(gearReverse); + + verify(mRearViewCameraViewController, times(1)).start(); + } + + @Test + public void testGearNonReverseStopsRearViewCamera() { + setUpListener(); + + int[] nonReverseVehicleGears = new int[]{ + VehicleGear.GEAR_NEUTRAL, VehicleGear.GEAR_PARK, VehicleGear.GEAR_DRIVE, + VehicleGear.GEAR_FIRST + }; + for (int i = 0; i < nonReverseVehicleGears.length; ++i) { + Log.i(TAG, "testGearNonReverseStopsRearViewCamera: gear=" + nonReverseVehicleGears[i]); + CarPropertyValue<Integer> propertyGear = new CarPropertyValue( + VehiclePropertyIds.GEAR_SELECTION, VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL, + nonReverseVehicleGears[i]); + mCarPropertyEventCallbackCaptor.getValue().onChangeEvent(propertyGear); + + verify(mRearViewCameraViewController, times(i + 1)).stop(); + } + } + + @Test + public void testBroadcastIntentStopsRearViewCamera() { + setUpListener(); + when(mRearViewCameraViewController.isShown()).thenReturn(true); + + Intent randomIntent = new Intent(Intent.ACTION_MAIN); + mBroadcastReceiverCaptor.getValue().onReceive(mContext, randomIntent); + + verify(mRearViewCameraViewController, never()).stop(); + + Intent actionCloseSystemDialogs = new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS); + mBroadcastReceiverCaptor.getValue().onReceive(mContext, actionCloseSystemDialogs); + + verify(mRearViewCameraViewController, times(1)).stop(); + } +} diff --git a/packages/FusedLocation/src/com/android/location/fused/FusedLocationProvider.java b/packages/FusedLocation/src/com/android/location/fused/FusedLocationProvider.java index 900e68d36c32..6827d6eb0180 100644 --- a/packages/FusedLocation/src/com/android/location/fused/FusedLocationProvider.java +++ b/packages/FusedLocation/src/com/android/location/fused/FusedLocationProvider.java @@ -19,6 +19,9 @@ package com.android.location.fused; import static android.content.Intent.ACTION_USER_SWITCHED; import static android.location.LocationManager.GPS_PROVIDER; import static android.location.LocationManager.NETWORK_PROVIDER; +import static android.location.LocationRequest.QUALITY_LOW_POWER; + +import static com.android.location.provider.ProviderRequestUnbundled.INTERVAL_DISABLED; import android.annotation.Nullable; import android.content.BroadcastReceiver; @@ -35,7 +38,6 @@ import android.os.WorkSource; import com.android.internal.annotations.GuardedBy; import com.android.internal.location.ProviderRequest; import com.android.location.provider.LocationProviderBase; -import com.android.location.provider.LocationRequestUnbundled; import com.android.location.provider.ProviderPropertiesUnbundled; import com.android.location.provider.ProviderRequestUnbundled; @@ -147,8 +149,8 @@ public class FusedLocationProvider extends LocationProviderBase { mRequest = new ProviderRequestUnbundled(ProviderRequest.EMPTY_REQUEST); mWorkSource = new WorkSource(); - mGpsInterval = Long.MAX_VALUE; - mNetworkInterval = Long.MAX_VALUE; + mGpsInterval = INTERVAL_DISABLED; + mNetworkInterval = INTERVAL_DISABLED; } void start() { @@ -175,30 +177,9 @@ public class FusedLocationProvider extends LocationProviderBase { @GuardedBy("mLock") private void updateRequirementsLocked() { - long gpsInterval = Long.MAX_VALUE; - long networkInterval = Long.MAX_VALUE; - if (mRequest.getReportLocation()) { - for (LocationRequestUnbundled request : mRequest.getLocationRequests()) { - switch (request.getQuality()) { - case LocationRequestUnbundled.ACCURACY_FINE: - case LocationRequestUnbundled.ACCURACY_BLOCK: - case LocationRequestUnbundled.POWER_HIGH: - if (request.getInterval() < gpsInterval) { - gpsInterval = request.getInterval(); - } - if (request.getInterval() < networkInterval) { - networkInterval = request.getInterval(); - } - break; - case LocationRequestUnbundled.ACCURACY_CITY: - case LocationRequestUnbundled.POWER_LOW: - if (request.getInterval() < networkInterval) { - networkInterval = request.getInterval(); - } - break; - } - } - } + long gpsInterval = mRequest.getQuality() < QUALITY_LOW_POWER ? mRequest.getInterval() + : INTERVAL_DISABLED; + long networkInterval = mRequest.getInterval(); if (gpsInterval != mGpsInterval) { resetProviderRequestLocked(GPS_PROVIDER, mGpsInterval, gpsInterval, mGpsListener); @@ -214,11 +195,12 @@ public class FusedLocationProvider extends LocationProviderBase { @GuardedBy("mLock") private void resetProviderRequestLocked(String provider, long oldInterval, long newInterval, LocationListener listener) { - if (oldInterval != Long.MAX_VALUE) { + if (oldInterval != INTERVAL_DISABLED && newInterval == INTERVAL_DISABLED) { mLocationManager.removeUpdates(listener); } - if (newInterval != Long.MAX_VALUE) { + if (newInterval != INTERVAL_DISABLED) { LocationRequest request = new LocationRequest.Builder(newInterval) + .setQuality(mRequest.getQuality()) .setLocationSettingsIgnored(mRequest.isLocationSettingsIgnored()) .setWorkSource(mWorkSource) .build(); @@ -254,10 +236,10 @@ public class FusedLocationProvider extends LocationProviderBase { void dump(PrintWriter writer) { synchronized (mLock) { writer.println("request: " + mRequest); - if (mGpsInterval != Long.MAX_VALUE) { + if (mGpsInterval != INTERVAL_DISABLED) { writer.println(" gps interval: " + mGpsInterval); } - if (mNetworkInterval != Long.MAX_VALUE) { + if (mNetworkInterval != INTERVAL_DISABLED) { writer.println(" network interval: " + mNetworkInterval); } if (mGpsLocation != null) { diff --git a/packages/FusedLocation/test/src/com/android/location/fused/tests/FusedLocationServiceTest.java b/packages/FusedLocation/test/src/com/android/location/fused/tests/FusedLocationServiceTest.java index d3aa977f85b1..61349d9bb8e8 100644 --- a/packages/FusedLocation/test/src/com/android/location/fused/tests/FusedLocationServiceTest.java +++ b/packages/FusedLocation/test/src/com/android/location/fused/tests/FusedLocationServiceTest.java @@ -48,7 +48,6 @@ import org.junit.runner.RunWith; import java.io.ByteArrayOutputStream; import java.io.FileInputStream; import java.io.IOException; -import java.util.Collections; import java.util.Random; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; @@ -60,7 +59,6 @@ public class FusedLocationServiceTest { private static final long TIMEOUT_MS = 5000; - private Context mContext; private Random mRandom; private LocationManager mLocationManager; @@ -72,15 +70,15 @@ public class FusedLocationServiceTest { long seed = System.currentTimeMillis(); Log.i(TAG, "location seed: " + seed); - mContext = InstrumentationRegistry.getTargetContext(); + Context context = InstrumentationRegistry.getTargetContext(); mRandom = new Random(seed); - mLocationManager = mContext.getSystemService(LocationManager.class); + mLocationManager = context.getSystemService(LocationManager.class); setMockLocation(true); mManager = new LocationProviderManagerCapture(); mProvider = ILocationProvider.Stub.asInterface( - new FusedLocationProvider(mContext).getBinder()); + new FusedLocationProvider(context).getBinder()); mProvider.setLocationProviderManager(mManager); mLocationManager.addTestProvider(NETWORK_PROVIDER, @@ -118,12 +116,9 @@ public class FusedLocationServiceTest { @Test public void testNetworkRequest() throws Exception { - LocationRequest request = new LocationRequest.Builder(1000).build(); - mProvider.setRequest( new ProviderRequest.Builder() .setIntervalMillis(1000) - .setLocationRequests(Collections.singletonList(request)) .build(), new WorkSource()); @@ -135,14 +130,10 @@ public class FusedLocationServiceTest { @Test public void testGpsRequest() throws Exception { - LocationRequest request = new LocationRequest.Builder(1000) - .setQuality(LocationRequest.POWER_HIGH) - .build(); - mProvider.setRequest( new ProviderRequest.Builder() + .setQuality(LocationRequest.QUALITY_HIGH_ACCURACY) .setIntervalMillis(1000) - .setLocationRequests(Collections.singletonList(request)) .build(), new WorkSource()); diff --git a/packages/InputDevices/res/values-af/strings.xml b/packages/InputDevices/res/values-af/strings.xml index 462a6a9bb7ac..7208894e1517 100644 --- a/packages/InputDevices/res/values-af/strings.xml +++ b/packages/InputDevices/res/values-af/strings.xml @@ -36,6 +36,7 @@ <string name="keyboard_layout_slovak" msgid="2469379934672837296">"Slowaaks"</string> <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Sloweens"</string> <string name="keyboard_layout_turkish" msgid="7736163250907964898">"Turks"</string> + <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Turks-F"</string> <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Oekraïens"</string> <string name="keyboard_layout_arabic" msgid="5671970465174968712">"Arabies"</string> <string name="keyboard_layout_greek" msgid="7289253560162386040">"Grieks"</string> diff --git a/packages/InputDevices/res/values-am/strings.xml b/packages/InputDevices/res/values-am/strings.xml index 1559fa890bcd..ff9f6520c324 100644 --- a/packages/InputDevices/res/values-am/strings.xml +++ b/packages/InputDevices/res/values-am/strings.xml @@ -36,6 +36,7 @@ <string name="keyboard_layout_slovak" msgid="2469379934672837296">"ስሎቫክ"</string> <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"ስሎቫኒያ"</string> <string name="keyboard_layout_turkish" msgid="7736163250907964898">"ቱርክኛ"</string> + <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"ቱርክኛ F"</string> <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"ዩክሬን"</string> <string name="keyboard_layout_arabic" msgid="5671970465174968712">"አረብኛ"</string> <string name="keyboard_layout_greek" msgid="7289253560162386040">"ግሪክኛ"</string> diff --git a/packages/InputDevices/res/values-ar/strings.xml b/packages/InputDevices/res/values-ar/strings.xml index bf508b2ad7d1..8ed19722c6ce 100644 --- a/packages/InputDevices/res/values-ar/strings.xml +++ b/packages/InputDevices/res/values-ar/strings.xml @@ -36,6 +36,7 @@ <string name="keyboard_layout_slovak" msgid="2469379934672837296">"السلوفاكية"</string> <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"السلوفينية"</string> <string name="keyboard_layout_turkish" msgid="7736163250907964898">"التركية"</string> + <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"لوحة المفاتيح باللغة التركية F"</string> <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"الأوكرانية"</string> <string name="keyboard_layout_arabic" msgid="5671970465174968712">"العربية"</string> <string name="keyboard_layout_greek" msgid="7289253560162386040">"اليونانية"</string> diff --git a/packages/InputDevices/res/values-as/strings.xml b/packages/InputDevices/res/values-as/strings.xml index 49fbef92c114..9744a7d39ef9 100644 --- a/packages/InputDevices/res/values-as/strings.xml +++ b/packages/InputDevices/res/values-as/strings.xml @@ -36,6 +36,7 @@ <string name="keyboard_layout_slovak" msgid="2469379934672837296">"শ্ল\'ভাক"</string> <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"শ্ল\'ভেনিয়া"</string> <string name="keyboard_layout_turkish" msgid="7736163250907964898">"তুৰ্কী"</string> + <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"টুৰ্কিছ F"</string> <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"ইউক্ৰেনিয়ান"</string> <string name="keyboard_layout_arabic" msgid="5671970465174968712">"আৰবী"</string> <string name="keyboard_layout_greek" msgid="7289253560162386040">"গ্ৰীক"</string> diff --git a/packages/InputDevices/res/values-az/strings.xml b/packages/InputDevices/res/values-az/strings.xml index c5a1e1ee2e2e..ee3a337e3ac4 100644 --- a/packages/InputDevices/res/values-az/strings.xml +++ b/packages/InputDevices/res/values-az/strings.xml @@ -36,6 +36,7 @@ <string name="keyboard_layout_slovak" msgid="2469379934672837296">"Slovak"</string> <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Sloven"</string> <string name="keyboard_layout_turkish" msgid="7736163250907964898">"Türk"</string> + <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Türkcə F"</string> <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Ukrayna"</string> <string name="keyboard_layout_arabic" msgid="5671970465174968712">"Ərəb"</string> <string name="keyboard_layout_greek" msgid="7289253560162386040">"Yunan"</string> diff --git a/packages/InputDevices/res/values-b+sr+Latn/strings.xml b/packages/InputDevices/res/values-b+sr+Latn/strings.xml index 16f1cb2fc591..1fc84f3aee11 100644 --- a/packages/InputDevices/res/values-b+sr+Latn/strings.xml +++ b/packages/InputDevices/res/values-b+sr+Latn/strings.xml @@ -36,6 +36,7 @@ <string name="keyboard_layout_slovak" msgid="2469379934672837296">"slovačka"</string> <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"slovenačka"</string> <string name="keyboard_layout_turkish" msgid="7736163250907964898">"turska"</string> + <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"turska F"</string> <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"ukrajinska"</string> <string name="keyboard_layout_arabic" msgid="5671970465174968712">"arapski"</string> <string name="keyboard_layout_greek" msgid="7289253560162386040">"grčki"</string> diff --git a/packages/InputDevices/res/values-be/strings.xml b/packages/InputDevices/res/values-be/strings.xml index 6b0523f2bdf8..50c591088563 100644 --- a/packages/InputDevices/res/values-be/strings.xml +++ b/packages/InputDevices/res/values-be/strings.xml @@ -36,6 +36,7 @@ <string name="keyboard_layout_slovak" msgid="2469379934672837296">"Славацкая"</string> <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Славенская"</string> <string name="keyboard_layout_turkish" msgid="7736163250907964898">"Турэцкая"</string> + <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Турэцкая-F"</string> <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Украінская"</string> <string name="keyboard_layout_arabic" msgid="5671970465174968712">"Арабская"</string> <string name="keyboard_layout_greek" msgid="7289253560162386040">"Грэчаская"</string> diff --git a/packages/InputDevices/res/values-bg/strings.xml b/packages/InputDevices/res/values-bg/strings.xml index a7088c930395..5c25f97f9cd5 100644 --- a/packages/InputDevices/res/values-bg/strings.xml +++ b/packages/InputDevices/res/values-bg/strings.xml @@ -36,6 +36,7 @@ <string name="keyboard_layout_slovak" msgid="2469379934672837296">"словашки"</string> <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"словенски"</string> <string name="keyboard_layout_turkish" msgid="7736163250907964898">"турски"</string> + <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Турски (тип F)"</string> <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"украински"</string> <string name="keyboard_layout_arabic" msgid="5671970465174968712">"Арабска клавиатурна подредба"</string> <string name="keyboard_layout_greek" msgid="7289253560162386040">"Гръцка клавиатурна подредба"</string> diff --git a/packages/InputDevices/res/values-bn/strings.xml b/packages/InputDevices/res/values-bn/strings.xml index f387414f50c8..a9965385fdbc 100644 --- a/packages/InputDevices/res/values-bn/strings.xml +++ b/packages/InputDevices/res/values-bn/strings.xml @@ -36,6 +36,7 @@ <string name="keyboard_layout_slovak" msgid="2469379934672837296">"স্লোভাক"</string> <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"স্লোভেনিয়ান"</string> <string name="keyboard_layout_turkish" msgid="7736163250907964898">"তুর্কি"</string> + <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Turkish F"</string> <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"ইউক্রেনীয়"</string> <string name="keyboard_layout_arabic" msgid="5671970465174968712">"আরবি"</string> <string name="keyboard_layout_greek" msgid="7289253560162386040">"গ্রীক"</string> diff --git a/packages/InputDevices/res/values-bs/strings.xml b/packages/InputDevices/res/values-bs/strings.xml index b92ac8cad498..df58464eefda 100644 --- a/packages/InputDevices/res/values-bs/strings.xml +++ b/packages/InputDevices/res/values-bs/strings.xml @@ -36,6 +36,7 @@ <string name="keyboard_layout_slovak" msgid="2469379934672837296">"slovački"</string> <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"slovenački"</string> <string name="keyboard_layout_turkish" msgid="7736163250907964898">"turski"</string> + <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"turski F"</string> <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"ukrajinski"</string> <string name="keyboard_layout_arabic" msgid="5671970465174968712">"arapski"</string> <string name="keyboard_layout_greek" msgid="7289253560162386040">"grčki"</string> diff --git a/packages/InputDevices/res/values-ca/strings.xml b/packages/InputDevices/res/values-ca/strings.xml index a5b5e10129d4..761c248bcdf7 100644 --- a/packages/InputDevices/res/values-ca/strings.xml +++ b/packages/InputDevices/res/values-ca/strings.xml @@ -36,6 +36,7 @@ <string name="keyboard_layout_slovak" msgid="2469379934672837296">"Eslovac"</string> <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Eslovè"</string> <string name="keyboard_layout_turkish" msgid="7736163250907964898">"Turc"</string> + <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Turc F"</string> <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Ucraïnès"</string> <string name="keyboard_layout_arabic" msgid="5671970465174968712">"Àrab"</string> <string name="keyboard_layout_greek" msgid="7289253560162386040">"Grec"</string> diff --git a/packages/InputDevices/res/values-cs/strings.xml b/packages/InputDevices/res/values-cs/strings.xml index 6b4f7ebf7a4a..3f1b3d0629bd 100644 --- a/packages/InputDevices/res/values-cs/strings.xml +++ b/packages/InputDevices/res/values-cs/strings.xml @@ -36,6 +36,7 @@ <string name="keyboard_layout_slovak" msgid="2469379934672837296">"slovenské"</string> <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"slovinské"</string> <string name="keyboard_layout_turkish" msgid="7736163250907964898">"turecké"</string> + <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"turečtina F"</string> <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"ukrajinské"</string> <string name="keyboard_layout_arabic" msgid="5671970465174968712">"arabština"</string> <string name="keyboard_layout_greek" msgid="7289253560162386040">"řečtina"</string> diff --git a/packages/InputDevices/res/values-da/strings.xml b/packages/InputDevices/res/values-da/strings.xml index cf2aecf1783a..b160341d003a 100644 --- a/packages/InputDevices/res/values-da/strings.xml +++ b/packages/InputDevices/res/values-da/strings.xml @@ -36,6 +36,7 @@ <string name="keyboard_layout_slovak" msgid="2469379934672837296">"Slovakisk"</string> <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Slovensk"</string> <string name="keyboard_layout_turkish" msgid="7736163250907964898">"Tyrkisk"</string> + <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Tyrkisk F"</string> <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Ukrainsk"</string> <string name="keyboard_layout_arabic" msgid="5671970465174968712">"Arabisk"</string> <string name="keyboard_layout_greek" msgid="7289253560162386040">"Græsk"</string> diff --git a/packages/InputDevices/res/values-de/strings.xml b/packages/InputDevices/res/values-de/strings.xml index 1e786855ac25..95bd806b8ffc 100644 --- a/packages/InputDevices/res/values-de/strings.xml +++ b/packages/InputDevices/res/values-de/strings.xml @@ -36,6 +36,7 @@ <string name="keyboard_layout_slovak" msgid="2469379934672837296">"Slowakisch"</string> <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Slowenisch"</string> <string name="keyboard_layout_turkish" msgid="7736163250907964898">"Türkisch"</string> + <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Türkisch F"</string> <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Ukrainisch"</string> <string name="keyboard_layout_arabic" msgid="5671970465174968712">"Arabisch"</string> <string name="keyboard_layout_greek" msgid="7289253560162386040">"Griechisch"</string> diff --git a/packages/InputDevices/res/values-el/strings.xml b/packages/InputDevices/res/values-el/strings.xml index eb2cc9b09a09..1b9b42e0f946 100644 --- a/packages/InputDevices/res/values-el/strings.xml +++ b/packages/InputDevices/res/values-el/strings.xml @@ -36,6 +36,7 @@ <string name="keyboard_layout_slovak" msgid="2469379934672837296">"Σλοβακικά"</string> <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Σλοβενικά"</string> <string name="keyboard_layout_turkish" msgid="7736163250907964898">"Τουρκικά"</string> + <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Τουρκικά F"</string> <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Ουκρανικά"</string> <string name="keyboard_layout_arabic" msgid="5671970465174968712">"Αραβικά"</string> <string name="keyboard_layout_greek" msgid="7289253560162386040">"Ελληνικά"</string> diff --git a/packages/InputDevices/res/values-en-rAU/strings.xml b/packages/InputDevices/res/values-en-rAU/strings.xml index f79c3daf25ef..ab48729d5006 100644 --- a/packages/InputDevices/res/values-en-rAU/strings.xml +++ b/packages/InputDevices/res/values-en-rAU/strings.xml @@ -36,6 +36,7 @@ <string name="keyboard_layout_slovak" msgid="2469379934672837296">"Slovak"</string> <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Slovenian"</string> <string name="keyboard_layout_turkish" msgid="7736163250907964898">"Turkish"</string> + <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Turkish F"</string> <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Ukrainian"</string> <string name="keyboard_layout_arabic" msgid="5671970465174968712">"Arabic"</string> <string name="keyboard_layout_greek" msgid="7289253560162386040">"Greek"</string> diff --git a/packages/InputDevices/res/values-en-rCA/strings.xml b/packages/InputDevices/res/values-en-rCA/strings.xml index f79c3daf25ef..ab48729d5006 100644 --- a/packages/InputDevices/res/values-en-rCA/strings.xml +++ b/packages/InputDevices/res/values-en-rCA/strings.xml @@ -36,6 +36,7 @@ <string name="keyboard_layout_slovak" msgid="2469379934672837296">"Slovak"</string> <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Slovenian"</string> <string name="keyboard_layout_turkish" msgid="7736163250907964898">"Turkish"</string> + <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Turkish F"</string> <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Ukrainian"</string> <string name="keyboard_layout_arabic" msgid="5671970465174968712">"Arabic"</string> <string name="keyboard_layout_greek" msgid="7289253560162386040">"Greek"</string> diff --git a/packages/InputDevices/res/values-en-rGB/strings.xml b/packages/InputDevices/res/values-en-rGB/strings.xml index f79c3daf25ef..ab48729d5006 100644 --- a/packages/InputDevices/res/values-en-rGB/strings.xml +++ b/packages/InputDevices/res/values-en-rGB/strings.xml @@ -36,6 +36,7 @@ <string name="keyboard_layout_slovak" msgid="2469379934672837296">"Slovak"</string> <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Slovenian"</string> <string name="keyboard_layout_turkish" msgid="7736163250907964898">"Turkish"</string> + <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Turkish F"</string> <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Ukrainian"</string> <string name="keyboard_layout_arabic" msgid="5671970465174968712">"Arabic"</string> <string name="keyboard_layout_greek" msgid="7289253560162386040">"Greek"</string> diff --git a/packages/InputDevices/res/values-en-rIN/strings.xml b/packages/InputDevices/res/values-en-rIN/strings.xml index f79c3daf25ef..ab48729d5006 100644 --- a/packages/InputDevices/res/values-en-rIN/strings.xml +++ b/packages/InputDevices/res/values-en-rIN/strings.xml @@ -36,6 +36,7 @@ <string name="keyboard_layout_slovak" msgid="2469379934672837296">"Slovak"</string> <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Slovenian"</string> <string name="keyboard_layout_turkish" msgid="7736163250907964898">"Turkish"</string> + <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Turkish F"</string> <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Ukrainian"</string> <string name="keyboard_layout_arabic" msgid="5671970465174968712">"Arabic"</string> <string name="keyboard_layout_greek" msgid="7289253560162386040">"Greek"</string> diff --git a/packages/InputDevices/res/values-en-rXC/strings.xml b/packages/InputDevices/res/values-en-rXC/strings.xml index d0881864efef..92c5a5c2f65e 100644 --- a/packages/InputDevices/res/values-en-rXC/strings.xml +++ b/packages/InputDevices/res/values-en-rXC/strings.xml @@ -36,6 +36,7 @@ <string name="keyboard_layout_slovak" msgid="2469379934672837296">"Slovak"</string> <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Slovenian"</string> <string name="keyboard_layout_turkish" msgid="7736163250907964898">"Turkish"</string> + <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Turkish F"</string> <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Ukrainian"</string> <string name="keyboard_layout_arabic" msgid="5671970465174968712">"Arabic"</string> <string name="keyboard_layout_greek" msgid="7289253560162386040">"Greek"</string> diff --git a/packages/InputDevices/res/values-es-rUS/strings.xml b/packages/InputDevices/res/values-es-rUS/strings.xml index e8d6597f0e57..b9fe046aa28c 100644 --- a/packages/InputDevices/res/values-es-rUS/strings.xml +++ b/packages/InputDevices/res/values-es-rUS/strings.xml @@ -36,6 +36,7 @@ <string name="keyboard_layout_slovak" msgid="2469379934672837296">"Eslovaco"</string> <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Esloveno"</string> <string name="keyboard_layout_turkish" msgid="7736163250907964898">"Turco"</string> + <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Turco F"</string> <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Ucraniano"</string> <string name="keyboard_layout_arabic" msgid="5671970465174968712">"Árabe"</string> <string name="keyboard_layout_greek" msgid="7289253560162386040">"Griego"</string> diff --git a/packages/InputDevices/res/values-es/strings.xml b/packages/InputDevices/res/values-es/strings.xml index 9396e460eba1..77b896bf6c5f 100644 --- a/packages/InputDevices/res/values-es/strings.xml +++ b/packages/InputDevices/res/values-es/strings.xml @@ -36,6 +36,7 @@ <string name="keyboard_layout_slovak" msgid="2469379934672837296">"Eslovaco"</string> <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Esloveno"</string> <string name="keyboard_layout_turkish" msgid="7736163250907964898">"Turco"</string> + <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Turco F"</string> <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Ucraniano"</string> <string name="keyboard_layout_arabic" msgid="5671970465174968712">"Árabe"</string> <string name="keyboard_layout_greek" msgid="7289253560162386040">"Griego"</string> diff --git a/packages/InputDevices/res/values-et/strings.xml b/packages/InputDevices/res/values-et/strings.xml index cf28e9f66dae..c83552234cc9 100644 --- a/packages/InputDevices/res/values-et/strings.xml +++ b/packages/InputDevices/res/values-et/strings.xml @@ -36,6 +36,7 @@ <string name="keyboard_layout_slovak" msgid="2469379934672837296">"Slovaki"</string> <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Sloveenia"</string> <string name="keyboard_layout_turkish" msgid="7736163250907964898">"Türgi"</string> + <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Türgi F"</string> <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Ukraina"</string> <string name="keyboard_layout_arabic" msgid="5671970465174968712">"Araabia"</string> <string name="keyboard_layout_greek" msgid="7289253560162386040">"Kreeka"</string> diff --git a/packages/InputDevices/res/values-eu/strings.xml b/packages/InputDevices/res/values-eu/strings.xml index 1e080fc83141..77d252d65a42 100644 --- a/packages/InputDevices/res/values-eu/strings.xml +++ b/packages/InputDevices/res/values-eu/strings.xml @@ -36,6 +36,7 @@ <string name="keyboard_layout_slovak" msgid="2469379934672837296">"Eslovakiarra"</string> <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Esloveniarra"</string> <string name="keyboard_layout_turkish" msgid="7736163250907964898">"Turkiarra"</string> + <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Turkiarra (F teklatua)"</string> <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Ukrainarra"</string> <string name="keyboard_layout_arabic" msgid="5671970465174968712">"Arabiarra"</string> <string name="keyboard_layout_greek" msgid="7289253560162386040">"Greziarra"</string> diff --git a/packages/InputDevices/res/values-fa/strings.xml b/packages/InputDevices/res/values-fa/strings.xml index 5cb237e35053..a086060a6a17 100644 --- a/packages/InputDevices/res/values-fa/strings.xml +++ b/packages/InputDevices/res/values-fa/strings.xml @@ -36,6 +36,7 @@ <string name="keyboard_layout_slovak" msgid="2469379934672837296">"اسلوواکی"</string> <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"اسلوونیایی"</string> <string name="keyboard_layout_turkish" msgid="7736163250907964898">"ترکی"</string> + <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Turkish F"</string> <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"اوکراینی"</string> <string name="keyboard_layout_arabic" msgid="5671970465174968712">"عربی"</string> <string name="keyboard_layout_greek" msgid="7289253560162386040">"یونانی"</string> diff --git a/packages/InputDevices/res/values-fi/strings.xml b/packages/InputDevices/res/values-fi/strings.xml index da7210657381..a20416f75714 100644 --- a/packages/InputDevices/res/values-fi/strings.xml +++ b/packages/InputDevices/res/values-fi/strings.xml @@ -36,6 +36,7 @@ <string name="keyboard_layout_slovak" msgid="2469379934672837296">"slovakki"</string> <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"sloveeni"</string> <string name="keyboard_layout_turkish" msgid="7736163250907964898">"turkki"</string> + <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"turkki (F)"</string> <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"ukraina"</string> <string name="keyboard_layout_arabic" msgid="5671970465174968712">"arabia"</string> <string name="keyboard_layout_greek" msgid="7289253560162386040">"kreikka"</string> diff --git a/packages/InputDevices/res/values-fr-rCA/strings.xml b/packages/InputDevices/res/values-fr-rCA/strings.xml index 45aca35eb012..636358248937 100644 --- a/packages/InputDevices/res/values-fr-rCA/strings.xml +++ b/packages/InputDevices/res/values-fr-rCA/strings.xml @@ -36,6 +36,7 @@ <string name="keyboard_layout_slovak" msgid="2469379934672837296">"Slovaque"</string> <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Slovène"</string> <string name="keyboard_layout_turkish" msgid="7736163250907964898">"Turc"</string> + <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Turc F"</string> <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Ukrainien"</string> <string name="keyboard_layout_arabic" msgid="5671970465174968712">"Arabe"</string> <string name="keyboard_layout_greek" msgid="7289253560162386040">"Grec"</string> diff --git a/packages/InputDevices/res/values-fr/strings.xml b/packages/InputDevices/res/values-fr/strings.xml index b55a3c99f410..0e41a2e98ab0 100644 --- a/packages/InputDevices/res/values-fr/strings.xml +++ b/packages/InputDevices/res/values-fr/strings.xml @@ -36,6 +36,7 @@ <string name="keyboard_layout_slovak" msgid="2469379934672837296">"Slovaque"</string> <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Slovène"</string> <string name="keyboard_layout_turkish" msgid="7736163250907964898">"Turc"</string> + <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Clavier turc en F"</string> <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Ukrainien"</string> <string name="keyboard_layout_arabic" msgid="5671970465174968712">"Arabe"</string> <string name="keyboard_layout_greek" msgid="7289253560162386040">"Grec"</string> diff --git a/packages/InputDevices/res/values-gl/strings.xml b/packages/InputDevices/res/values-gl/strings.xml index 40ede04c8e05..9995f79ee732 100644 --- a/packages/InputDevices/res/values-gl/strings.xml +++ b/packages/InputDevices/res/values-gl/strings.xml @@ -36,6 +36,7 @@ <string name="keyboard_layout_slovak" msgid="2469379934672837296">"Eslovaco"</string> <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Esloveno"</string> <string name="keyboard_layout_turkish" msgid="7736163250907964898">"Turco"</string> + <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Turco F"</string> <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Ucraíno"</string> <string name="keyboard_layout_arabic" msgid="5671970465174968712">"Árabe"</string> <string name="keyboard_layout_greek" msgid="7289253560162386040">"Grego"</string> diff --git a/packages/InputDevices/res/values-gu/strings.xml b/packages/InputDevices/res/values-gu/strings.xml index 631fc1497ce4..de50fb920965 100644 --- a/packages/InputDevices/res/values-gu/strings.xml +++ b/packages/InputDevices/res/values-gu/strings.xml @@ -36,6 +36,7 @@ <string name="keyboard_layout_slovak" msgid="2469379934672837296">"સ્લોવૅક"</string> <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"સ્લોવેનિયન"</string> <string name="keyboard_layout_turkish" msgid="7736163250907964898">"ટર્કીશ"</string> + <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"ટર્કિશ F"</string> <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"યુક્રેનિયન"</string> <string name="keyboard_layout_arabic" msgid="5671970465174968712">"અરબી"</string> <string name="keyboard_layout_greek" msgid="7289253560162386040">"ગ્રીક"</string> diff --git a/packages/InputDevices/res/values-hi/strings.xml b/packages/InputDevices/res/values-hi/strings.xml index 7d1e2f8106e7..55fc5bfb6152 100644 --- a/packages/InputDevices/res/values-hi/strings.xml +++ b/packages/InputDevices/res/values-hi/strings.xml @@ -36,6 +36,7 @@ <string name="keyboard_layout_slovak" msgid="2469379934672837296">"स्लोवाक"</string> <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"स्लोवेनियाई"</string> <string name="keyboard_layout_turkish" msgid="7736163250907964898">"तुर्की"</string> + <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Turkish F"</string> <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"यूक्रेनियाई"</string> <string name="keyboard_layout_arabic" msgid="5671970465174968712">"अरबी"</string> <string name="keyboard_layout_greek" msgid="7289253560162386040">"ग्रीक"</string> diff --git a/packages/InputDevices/res/values-hr/strings.xml b/packages/InputDevices/res/values-hr/strings.xml index aff2a37c2b7f..6832437e1a4a 100644 --- a/packages/InputDevices/res/values-hr/strings.xml +++ b/packages/InputDevices/res/values-hr/strings.xml @@ -36,6 +36,7 @@ <string name="keyboard_layout_slovak" msgid="2469379934672837296">"slovačka"</string> <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"slovenska"</string> <string name="keyboard_layout_turkish" msgid="7736163250907964898">"turska"</string> + <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"turski F"</string> <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"ukrajinska"</string> <string name="keyboard_layout_arabic" msgid="5671970465174968712">"arapski"</string> <string name="keyboard_layout_greek" msgid="7289253560162386040">"grčki"</string> diff --git a/packages/InputDevices/res/values-hu/strings.xml b/packages/InputDevices/res/values-hu/strings.xml index 50d667b40b62..f2ac8a2b8085 100644 --- a/packages/InputDevices/res/values-hu/strings.xml +++ b/packages/InputDevices/res/values-hu/strings.xml @@ -36,6 +36,7 @@ <string name="keyboard_layout_slovak" msgid="2469379934672837296">"szlovák"</string> <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"szlovén"</string> <string name="keyboard_layout_turkish" msgid="7736163250907964898">"török"</string> + <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"török F-billentyűzet"</string> <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"ukrán"</string> <string name="keyboard_layout_arabic" msgid="5671970465174968712">"arab"</string> <string name="keyboard_layout_greek" msgid="7289253560162386040">"görög"</string> diff --git a/packages/InputDevices/res/values-hy/strings.xml b/packages/InputDevices/res/values-hy/strings.xml index 4a6fe2b3be4f..a6676fe9875a 100644 --- a/packages/InputDevices/res/values-hy/strings.xml +++ b/packages/InputDevices/res/values-hy/strings.xml @@ -36,6 +36,7 @@ <string name="keyboard_layout_slovak" msgid="2469379934672837296">"Սլովակերեն"</string> <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Սլովեներեն"</string> <string name="keyboard_layout_turkish" msgid="7736163250907964898">"Թուրքերեն"</string> + <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"թուրքերեն F"</string> <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Ուկրաիներեն"</string> <string name="keyboard_layout_arabic" msgid="5671970465174968712">"Արաբերեն"</string> <string name="keyboard_layout_greek" msgid="7289253560162386040">"Հունարեն"</string> diff --git a/packages/InputDevices/res/values-in/strings.xml b/packages/InputDevices/res/values-in/strings.xml index 90ba97d16f6a..41bf2decd44f 100644 --- a/packages/InputDevices/res/values-in/strings.xml +++ b/packages/InputDevices/res/values-in/strings.xml @@ -36,6 +36,7 @@ <string name="keyboard_layout_slovak" msgid="2469379934672837296">"Slovakia"</string> <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Slovenia"</string> <string name="keyboard_layout_turkish" msgid="7736163250907964898">"Turki"</string> + <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Turki F"</string> <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Ukraina"</string> <string name="keyboard_layout_arabic" msgid="5671970465174968712">"Arab"</string> <string name="keyboard_layout_greek" msgid="7289253560162386040">"Yunani"</string> diff --git a/packages/InputDevices/res/values-is/strings.xml b/packages/InputDevices/res/values-is/strings.xml index 0889b21f3a64..b761a7eb36f0 100644 --- a/packages/InputDevices/res/values-is/strings.xml +++ b/packages/InputDevices/res/values-is/strings.xml @@ -36,6 +36,7 @@ <string name="keyboard_layout_slovak" msgid="2469379934672837296">"Slóvaskt"</string> <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Slóvenskt"</string> <string name="keyboard_layout_turkish" msgid="7736163250907964898">"Tyrkneskt"</string> + <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Tyrkneskt F"</string> <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Úkranískt"</string> <string name="keyboard_layout_arabic" msgid="5671970465174968712">"Arabískt"</string> <string name="keyboard_layout_greek" msgid="7289253560162386040">"Grískt"</string> diff --git a/packages/InputDevices/res/values-it/strings.xml b/packages/InputDevices/res/values-it/strings.xml index 77f78c6f49d0..ed1ec557dd88 100644 --- a/packages/InputDevices/res/values-it/strings.xml +++ b/packages/InputDevices/res/values-it/strings.xml @@ -36,6 +36,7 @@ <string name="keyboard_layout_slovak" msgid="2469379934672837296">"Slovacco"</string> <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Sloveno"</string> <string name="keyboard_layout_turkish" msgid="7736163250907964898">"Turco"</string> + <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Turco F"</string> <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Ucraino"</string> <string name="keyboard_layout_arabic" msgid="5671970465174968712">"Arabo"</string> <string name="keyboard_layout_greek" msgid="7289253560162386040">"Greco"</string> diff --git a/packages/InputDevices/res/values-iw/strings.xml b/packages/InputDevices/res/values-iw/strings.xml index 52641b2ba1ff..de9b2767dbd6 100644 --- a/packages/InputDevices/res/values-iw/strings.xml +++ b/packages/InputDevices/res/values-iw/strings.xml @@ -36,6 +36,7 @@ <string name="keyboard_layout_slovak" msgid="2469379934672837296">"סלובקית"</string> <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"סלובנית"</string> <string name="keyboard_layout_turkish" msgid="7736163250907964898">"טורקית"</string> + <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"טורקית F"</string> <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"אוקראינית"</string> <string name="keyboard_layout_arabic" msgid="5671970465174968712">"ערבית"</string> <string name="keyboard_layout_greek" msgid="7289253560162386040">"יוונית"</string> diff --git a/packages/InputDevices/res/values-ja/strings.xml b/packages/InputDevices/res/values-ja/strings.xml index 2961548599a6..6be0b4c7a8e7 100644 --- a/packages/InputDevices/res/values-ja/strings.xml +++ b/packages/InputDevices/res/values-ja/strings.xml @@ -36,6 +36,7 @@ <string name="keyboard_layout_slovak" msgid="2469379934672837296">"スロバキア語"</string> <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"スロベニア語"</string> <string name="keyboard_layout_turkish" msgid="7736163250907964898">"トルコ語"</string> + <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"トルコ語 F"</string> <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"ウクライナ語"</string> <string name="keyboard_layout_arabic" msgid="5671970465174968712">"アラビア語"</string> <string name="keyboard_layout_greek" msgid="7289253560162386040">"ギリシャ語"</string> diff --git a/packages/InputDevices/res/values-ka/strings.xml b/packages/InputDevices/res/values-ka/strings.xml index 2ccfeb2f635f..92d94705b0fe 100644 --- a/packages/InputDevices/res/values-ka/strings.xml +++ b/packages/InputDevices/res/values-ka/strings.xml @@ -36,6 +36,7 @@ <string name="keyboard_layout_slovak" msgid="2469379934672837296">"სლოვაკური"</string> <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"სლოვენური"</string> <string name="keyboard_layout_turkish" msgid="7736163250907964898">"თურქული"</string> + <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"თურქული F"</string> <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"უკრაინული"</string> <string name="keyboard_layout_arabic" msgid="5671970465174968712">"არაბული"</string> <string name="keyboard_layout_greek" msgid="7289253560162386040">"ბერძნული"</string> diff --git a/packages/InputDevices/res/values-kk/strings.xml b/packages/InputDevices/res/values-kk/strings.xml index dfe8c56802c7..c8ab7968d844 100644 --- a/packages/InputDevices/res/values-kk/strings.xml +++ b/packages/InputDevices/res/values-kk/strings.xml @@ -36,6 +36,7 @@ <string name="keyboard_layout_slovak" msgid="2469379934672837296">"Словак"</string> <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Словен"</string> <string name="keyboard_layout_turkish" msgid="7736163250907964898">"Түрік"</string> + <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Түрік тілі, F"</string> <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Украин"</string> <string name="keyboard_layout_arabic" msgid="5671970465174968712">"Араб"</string> <string name="keyboard_layout_greek" msgid="7289253560162386040">"Грек"</string> diff --git a/packages/InputDevices/res/values-km/strings.xml b/packages/InputDevices/res/values-km/strings.xml index 3bd7f202f287..4b123214a056 100644 --- a/packages/InputDevices/res/values-km/strings.xml +++ b/packages/InputDevices/res/values-km/strings.xml @@ -36,6 +36,7 @@ <string name="keyboard_layout_slovak" msgid="2469379934672837296">"ស្លូវ៉ាគី"</string> <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"ស្លូវ៉ានី"</string> <string name="keyboard_layout_turkish" msgid="7736163250907964898">"ទួរគី"</string> + <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"តួកគី F"</string> <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"អ៊ុយក្រែន"</string> <string name="keyboard_layout_arabic" msgid="5671970465174968712">"អារ៉ាប់"</string> <string name="keyboard_layout_greek" msgid="7289253560162386040">"ក្រិក"</string> diff --git a/packages/InputDevices/res/values-kn/strings.xml b/packages/InputDevices/res/values-kn/strings.xml index 1e3c6932c3d4..761b7ccef00e 100644 --- a/packages/InputDevices/res/values-kn/strings.xml +++ b/packages/InputDevices/res/values-kn/strings.xml @@ -36,6 +36,7 @@ <string name="keyboard_layout_slovak" msgid="2469379934672837296">"ಸ್ಲೋವಾಕ್"</string> <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"ಸ್ಲೋವೇನಿಯನ್"</string> <string name="keyboard_layout_turkish" msgid="7736163250907964898">"ಟರ್ಕಿಶ್"</string> + <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"ಟರ್ಕಿಶ್ F"</string> <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"ಉಕ್ರೇನಿಯನ್"</string> <string name="keyboard_layout_arabic" msgid="5671970465174968712">"ಅರೇಬಿಕ್"</string> <string name="keyboard_layout_greek" msgid="7289253560162386040">"ಗ್ರೀಕ್"</string> diff --git a/packages/InputDevices/res/values-ko/strings.xml b/packages/InputDevices/res/values-ko/strings.xml index 147050462eab..2a1cbb00174b 100644 --- a/packages/InputDevices/res/values-ko/strings.xml +++ b/packages/InputDevices/res/values-ko/strings.xml @@ -36,6 +36,7 @@ <string name="keyboard_layout_slovak" msgid="2469379934672837296">"슬로바키아어"</string> <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"슬로베니아어"</string> <string name="keyboard_layout_turkish" msgid="7736163250907964898">"터키어"</string> + <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"터키어 F"</string> <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"우크라이나어"</string> <string name="keyboard_layout_arabic" msgid="5671970465174968712">"아랍어"</string> <string name="keyboard_layout_greek" msgid="7289253560162386040">"그리스어"</string> diff --git a/packages/InputDevices/res/values-ky/strings.xml b/packages/InputDevices/res/values-ky/strings.xml index cb9dbb2f0abb..70295e1859a8 100644 --- a/packages/InputDevices/res/values-ky/strings.xml +++ b/packages/InputDevices/res/values-ky/strings.xml @@ -36,6 +36,7 @@ <string name="keyboard_layout_slovak" msgid="2469379934672837296">"Словак"</string> <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Словен"</string> <string name="keyboard_layout_turkish" msgid="7736163250907964898">"түркчө"</string> + <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Түркчө F"</string> <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Украин"</string> <string name="keyboard_layout_arabic" msgid="5671970465174968712">"Арабча"</string> <string name="keyboard_layout_greek" msgid="7289253560162386040">"Грекче"</string> diff --git a/packages/InputDevices/res/values-lo/strings.xml b/packages/InputDevices/res/values-lo/strings.xml index 4ae4b7d9c58b..2b8946b43d64 100644 --- a/packages/InputDevices/res/values-lo/strings.xml +++ b/packages/InputDevices/res/values-lo/strings.xml @@ -36,6 +36,7 @@ <string name="keyboard_layout_slovak" msgid="2469379934672837296">"ສະໂລແວັກ"</string> <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"ສະໂລເວນຽນ"</string> <string name="keyboard_layout_turkish" msgid="7736163250907964898">"ເຕີກິສ"</string> + <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"ເທືຄິຊ-F"</string> <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"ຢູເຄຣນຽນ"</string> <string name="keyboard_layout_arabic" msgid="5671970465174968712">"ອາຣັບ"</string> <string name="keyboard_layout_greek" msgid="7289253560162386040">"ກຣີກ"</string> diff --git a/packages/InputDevices/res/values-lt/strings.xml b/packages/InputDevices/res/values-lt/strings.xml index d2aef7f057db..5fca46bf3e5a 100644 --- a/packages/InputDevices/res/values-lt/strings.xml +++ b/packages/InputDevices/res/values-lt/strings.xml @@ -36,6 +36,7 @@ <string name="keyboard_layout_slovak" msgid="2469379934672837296">"Slovakų k."</string> <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Slovėnų k."</string> <string name="keyboard_layout_turkish" msgid="7736163250907964898">"Turkų k."</string> + <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Turkų F k."</string> <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Ukrainiečių k."</string> <string name="keyboard_layout_arabic" msgid="5671970465174968712">"Arabų"</string> <string name="keyboard_layout_greek" msgid="7289253560162386040">"Graikų"</string> diff --git a/packages/InputDevices/res/values-lv/strings.xml b/packages/InputDevices/res/values-lv/strings.xml index 8f3ff0ab2264..d2253297c8ff 100644 --- a/packages/InputDevices/res/values-lv/strings.xml +++ b/packages/InputDevices/res/values-lv/strings.xml @@ -36,6 +36,7 @@ <string name="keyboard_layout_slovak" msgid="2469379934672837296">"Slovāku"</string> <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Slovēņu"</string> <string name="keyboard_layout_turkish" msgid="7736163250907964898">"Turku"</string> + <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Turku F"</string> <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Ukraiņu"</string> <string name="keyboard_layout_arabic" msgid="5671970465174968712">"Arābu"</string> <string name="keyboard_layout_greek" msgid="7289253560162386040">"Grieķu"</string> diff --git a/packages/InputDevices/res/values-mk/strings.xml b/packages/InputDevices/res/values-mk/strings.xml index 9584e156ad9c..d98b58b8d799 100644 --- a/packages/InputDevices/res/values-mk/strings.xml +++ b/packages/InputDevices/res/values-mk/strings.xml @@ -36,6 +36,7 @@ <string name="keyboard_layout_slovak" msgid="2469379934672837296">"Словачки"</string> <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Словенечки"</string> <string name="keyboard_layout_turkish" msgid="7736163250907964898">"Турски"</string> + <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Турски F"</string> <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Украински"</string> <string name="keyboard_layout_arabic" msgid="5671970465174968712">"арапски"</string> <string name="keyboard_layout_greek" msgid="7289253560162386040">"грчки"</string> diff --git a/packages/InputDevices/res/values-ml/strings.xml b/packages/InputDevices/res/values-ml/strings.xml index 9e5344367eae..f3468810decd 100644 --- a/packages/InputDevices/res/values-ml/strings.xml +++ b/packages/InputDevices/res/values-ml/strings.xml @@ -36,6 +36,7 @@ <string name="keyboard_layout_slovak" msgid="2469379934672837296">"സ്ലോവാക്"</string> <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"സ്ലോവേനിയൻ"</string> <string name="keyboard_layout_turkish" msgid="7736163250907964898">"ടർക്കിഷ്"</string> + <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"ടർക്കിഷ്-എഫ്"</string> <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"ഉക്രേനിയന്"</string> <string name="keyboard_layout_arabic" msgid="5671970465174968712">"അറബിക്"</string> <string name="keyboard_layout_greek" msgid="7289253560162386040">"ഗ്രീക്ക്"</string> diff --git a/packages/InputDevices/res/values-mn/strings.xml b/packages/InputDevices/res/values-mn/strings.xml index 18c2faf7e464..fcaf321c6b6c 100644 --- a/packages/InputDevices/res/values-mn/strings.xml +++ b/packages/InputDevices/res/values-mn/strings.xml @@ -36,6 +36,7 @@ <string name="keyboard_layout_slovak" msgid="2469379934672837296">"Словак"</string> <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Словени"</string> <string name="keyboard_layout_turkish" msgid="7736163250907964898">"Турк"</string> + <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Турк F"</string> <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Украйн"</string> <string name="keyboard_layout_arabic" msgid="5671970465174968712">"Араб"</string> <string name="keyboard_layout_greek" msgid="7289253560162386040">"Грек"</string> diff --git a/packages/InputDevices/res/values-mr/strings.xml b/packages/InputDevices/res/values-mr/strings.xml index d8788c97b90c..c04006d82122 100644 --- a/packages/InputDevices/res/values-mr/strings.xml +++ b/packages/InputDevices/res/values-mr/strings.xml @@ -36,6 +36,7 @@ <string name="keyboard_layout_slovak" msgid="2469379934672837296">"स्लोव्हाक"</string> <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"स्लोव्हेनियन"</string> <string name="keyboard_layout_turkish" msgid="7736163250907964898">"तुर्की"</string> + <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"तुर्कीश एफ"</string> <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"यूक्रेनियन"</string> <string name="keyboard_layout_arabic" msgid="5671970465174968712">"अरबी"</string> <string name="keyboard_layout_greek" msgid="7289253560162386040">"ग्रीक"</string> diff --git a/packages/InputDevices/res/values-ms/strings.xml b/packages/InputDevices/res/values-ms/strings.xml index 8bc9b2a73cb2..9bff171385ee 100644 --- a/packages/InputDevices/res/values-ms/strings.xml +++ b/packages/InputDevices/res/values-ms/strings.xml @@ -36,6 +36,7 @@ <string name="keyboard_layout_slovak" msgid="2469379934672837296">"Bahasa Slovakia"</string> <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Bahasa Slovenia"</string> <string name="keyboard_layout_turkish" msgid="7736163250907964898">"Bahasa Turki"</string> + <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Turki F"</string> <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Bahasa Ukraine"</string> <string name="keyboard_layout_arabic" msgid="5671970465174968712">"Bahasa Arab"</string> <string name="keyboard_layout_greek" msgid="7289253560162386040">"Bahasa Greek"</string> diff --git a/packages/InputDevices/res/values-my/strings.xml b/packages/InputDevices/res/values-my/strings.xml index 26720576ee91..01b55074d9f7 100644 --- a/packages/InputDevices/res/values-my/strings.xml +++ b/packages/InputDevices/res/values-my/strings.xml @@ -36,6 +36,7 @@ <string name="keyboard_layout_slovak" msgid="2469379934672837296">"စလိုဗက်"</string> <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"စလိုဗေးနီးယန်း"</string> <string name="keyboard_layout_turkish" msgid="7736163250907964898">"တူရကီ"</string> + <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"တူရကီ F"</string> <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"ယူကရိန်း"</string> <string name="keyboard_layout_arabic" msgid="5671970465174968712">"အာရပ်"</string> <string name="keyboard_layout_greek" msgid="7289253560162386040">"ဂရိ"</string> diff --git a/packages/InputDevices/res/values-nb/strings.xml b/packages/InputDevices/res/values-nb/strings.xml index 83b87e5dd45e..60cac3d38f00 100644 --- a/packages/InputDevices/res/values-nb/strings.xml +++ b/packages/InputDevices/res/values-nb/strings.xml @@ -36,6 +36,7 @@ <string name="keyboard_layout_slovak" msgid="2469379934672837296">"Slovakisk"</string> <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Slovensk"</string> <string name="keyboard_layout_turkish" msgid="7736163250907964898">"Tyrkisk"</string> + <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Tyrkisk F"</string> <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Ukrainsk"</string> <string name="keyboard_layout_arabic" msgid="5671970465174968712">"Arabisk"</string> <string name="keyboard_layout_greek" msgid="7289253560162386040">"Gresk"</string> diff --git a/packages/InputDevices/res/values-ne/strings.xml b/packages/InputDevices/res/values-ne/strings.xml index 4801f752fa6c..13740e7f1dd2 100644 --- a/packages/InputDevices/res/values-ne/strings.xml +++ b/packages/InputDevices/res/values-ne/strings.xml @@ -36,6 +36,7 @@ <string name="keyboard_layout_slovak" msgid="2469379934672837296">"स्लोवाक"</string> <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"स्लोवेनियाई"</string> <string name="keyboard_layout_turkish" msgid="7736163250907964898">"टर्किश"</string> + <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"तुर्किस-F"</string> <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"युक्रेनी"</string> <string name="keyboard_layout_arabic" msgid="5671970465174968712">"अरबी"</string> <string name="keyboard_layout_greek" msgid="7289253560162386040">"ग्रीक"</string> diff --git a/packages/InputDevices/res/values-nl/strings.xml b/packages/InputDevices/res/values-nl/strings.xml index 6e5849013fe7..f3a58142f461 100644 --- a/packages/InputDevices/res/values-nl/strings.xml +++ b/packages/InputDevices/res/values-nl/strings.xml @@ -36,6 +36,7 @@ <string name="keyboard_layout_slovak" msgid="2469379934672837296">"Slowaaks"</string> <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Sloveens"</string> <string name="keyboard_layout_turkish" msgid="7736163250907964898">"Turks"</string> + <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Turks F"</string> <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Oekraïens"</string> <string name="keyboard_layout_arabic" msgid="5671970465174968712">"Arabisch"</string> <string name="keyboard_layout_greek" msgid="7289253560162386040">"Grieks"</string> diff --git a/packages/InputDevices/res/values-or/strings.xml b/packages/InputDevices/res/values-or/strings.xml index aa16151752e6..52556ef8979f 100644 --- a/packages/InputDevices/res/values-or/strings.xml +++ b/packages/InputDevices/res/values-or/strings.xml @@ -36,6 +36,7 @@ <string name="keyboard_layout_slovak" msgid="2469379934672837296">"ସ୍ଲୋଭାକ୍"</string> <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"ସ୍ଲୋଭେନିଆନ୍"</string> <string name="keyboard_layout_turkish" msgid="7736163250907964898">"ତୁର୍କିସ୍"</string> + <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"ତୁର୍କିଶ୍ F"</string> <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"ୟୁକ୍ରାନିଆନ୍"</string> <string name="keyboard_layout_arabic" msgid="5671970465174968712">"ଆରବିକ୍"</string> <string name="keyboard_layout_greek" msgid="7289253560162386040">"ଗ୍ରୀକ୍"</string> diff --git a/packages/InputDevices/res/values-pa/strings.xml b/packages/InputDevices/res/values-pa/strings.xml index 7e5a03cde986..f261fb526c6f 100644 --- a/packages/InputDevices/res/values-pa/strings.xml +++ b/packages/InputDevices/res/values-pa/strings.xml @@ -36,6 +36,7 @@ <string name="keyboard_layout_slovak" msgid="2469379934672837296">"ਸਲੋਵਾਕ"</string> <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"ਸਲੋਵੀਅਨ"</string> <string name="keyboard_layout_turkish" msgid="7736163250907964898">"ਤੁਰਕੀ"</string> + <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"ਤੁਰਕੀ F"</string> <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"ਯੂਕਰੇਨੀਅਨ"</string> <string name="keyboard_layout_arabic" msgid="5671970465174968712">"ਅਰਬੀ"</string> <string name="keyboard_layout_greek" msgid="7289253560162386040">"ਯੂਨਾਨੀ"</string> diff --git a/packages/InputDevices/res/values-pl/strings.xml b/packages/InputDevices/res/values-pl/strings.xml index 51755e2b2b52..25a3a90701e0 100644 --- a/packages/InputDevices/res/values-pl/strings.xml +++ b/packages/InputDevices/res/values-pl/strings.xml @@ -36,6 +36,7 @@ <string name="keyboard_layout_slovak" msgid="2469379934672837296">"Słowacki"</string> <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Słoweński"</string> <string name="keyboard_layout_turkish" msgid="7736163250907964898">"Turecki"</string> + <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Turecka F"</string> <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Ukraiński"</string> <string name="keyboard_layout_arabic" msgid="5671970465174968712">"arabski"</string> <string name="keyboard_layout_greek" msgid="7289253560162386040">"grecki"</string> diff --git a/packages/InputDevices/res/values-pt-rBR/strings.xml b/packages/InputDevices/res/values-pt-rBR/strings.xml index 128868856f5a..e44f4ae131f9 100644 --- a/packages/InputDevices/res/values-pt-rBR/strings.xml +++ b/packages/InputDevices/res/values-pt-rBR/strings.xml @@ -36,6 +36,7 @@ <string name="keyboard_layout_slovak" msgid="2469379934672837296">"Eslovaco"</string> <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Esloveno"</string> <string name="keyboard_layout_turkish" msgid="7736163250907964898">"Turco"</string> + <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Turco F"</string> <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Ucraniano"</string> <string name="keyboard_layout_arabic" msgid="5671970465174968712">"Árabe"</string> <string name="keyboard_layout_greek" msgid="7289253560162386040">"Grego"</string> diff --git a/packages/InputDevices/res/values-pt-rPT/strings.xml b/packages/InputDevices/res/values-pt-rPT/strings.xml index 89bb3e376678..3ad3e638a680 100644 --- a/packages/InputDevices/res/values-pt-rPT/strings.xml +++ b/packages/InputDevices/res/values-pt-rPT/strings.xml @@ -36,6 +36,7 @@ <string name="keyboard_layout_slovak" msgid="2469379934672837296">"Eslovaco"</string> <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Esloveno"</string> <string name="keyboard_layout_turkish" msgid="7736163250907964898">"Turco"</string> + <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Turco F"</string> <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Ucraniano"</string> <string name="keyboard_layout_arabic" msgid="5671970465174968712">"Árabe"</string> <string name="keyboard_layout_greek" msgid="7289253560162386040">"Grego"</string> diff --git a/packages/InputDevices/res/values-pt/strings.xml b/packages/InputDevices/res/values-pt/strings.xml index 128868856f5a..e44f4ae131f9 100644 --- a/packages/InputDevices/res/values-pt/strings.xml +++ b/packages/InputDevices/res/values-pt/strings.xml @@ -36,6 +36,7 @@ <string name="keyboard_layout_slovak" msgid="2469379934672837296">"Eslovaco"</string> <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Esloveno"</string> <string name="keyboard_layout_turkish" msgid="7736163250907964898">"Turco"</string> + <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Turco F"</string> <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Ucraniano"</string> <string name="keyboard_layout_arabic" msgid="5671970465174968712">"Árabe"</string> <string name="keyboard_layout_greek" msgid="7289253560162386040">"Grego"</string> diff --git a/packages/InputDevices/res/values-ro/strings.xml b/packages/InputDevices/res/values-ro/strings.xml index f7ff2504f31f..3867e1cae1d6 100644 --- a/packages/InputDevices/res/values-ro/strings.xml +++ b/packages/InputDevices/res/values-ro/strings.xml @@ -36,6 +36,7 @@ <string name="keyboard_layout_slovak" msgid="2469379934672837296">"Slovacă"</string> <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Slovenă"</string> <string name="keyboard_layout_turkish" msgid="7736163250907964898">"Turcă"</string> + <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Turcă F"</string> <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Ucraineană"</string> <string name="keyboard_layout_arabic" msgid="5671970465174968712">"Arabă"</string> <string name="keyboard_layout_greek" msgid="7289253560162386040">"Greacă"</string> diff --git a/packages/InputDevices/res/values-ru/strings.xml b/packages/InputDevices/res/values-ru/strings.xml index 70668133ae8b..7c5c95aebe68 100644 --- a/packages/InputDevices/res/values-ru/strings.xml +++ b/packages/InputDevices/res/values-ru/strings.xml @@ -36,6 +36,7 @@ <string name="keyboard_layout_slovak" msgid="2469379934672837296">"словацкий"</string> <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"словенский"</string> <string name="keyboard_layout_turkish" msgid="7736163250907964898">"турецкий"</string> + <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"турецкий (тип F)"</string> <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"украинский"</string> <string name="keyboard_layout_arabic" msgid="5671970465174968712">"арабский"</string> <string name="keyboard_layout_greek" msgid="7289253560162386040">"греческий"</string> diff --git a/packages/InputDevices/res/values-si/strings.xml b/packages/InputDevices/res/values-si/strings.xml index eb3c44678db2..f4147f3008ee 100644 --- a/packages/InputDevices/res/values-si/strings.xml +++ b/packages/InputDevices/res/values-si/strings.xml @@ -36,6 +36,7 @@ <string name="keyboard_layout_slovak" msgid="2469379934672837296">"ස්ලෝවැක්"</string> <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"ස්ලෝවේනියානු"</string> <string name="keyboard_layout_turkish" msgid="7736163250907964898">"තුර්කි"</string> + <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"තුර්කි F"</string> <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"යුක්රේනියානු"</string> <string name="keyboard_layout_arabic" msgid="5671970465174968712">"අරාබි"</string> <string name="keyboard_layout_greek" msgid="7289253560162386040">"ග්රීක"</string> diff --git a/packages/InputDevices/res/values-sk/strings.xml b/packages/InputDevices/res/values-sk/strings.xml index e7e15b0cbd00..301c800a933c 100644 --- a/packages/InputDevices/res/values-sk/strings.xml +++ b/packages/InputDevices/res/values-sk/strings.xml @@ -36,6 +36,7 @@ <string name="keyboard_layout_slovak" msgid="2469379934672837296">"slovenské"</string> <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"slovinské"</string> <string name="keyboard_layout_turkish" msgid="7736163250907964898">"turecké"</string> + <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Turečtina F"</string> <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"ukrajinské"</string> <string name="keyboard_layout_arabic" msgid="5671970465174968712">"Arabčina"</string> <string name="keyboard_layout_greek" msgid="7289253560162386040">"Gréčtina"</string> diff --git a/packages/InputDevices/res/values-sl/strings.xml b/packages/InputDevices/res/values-sl/strings.xml index f4d1e5732fc5..09b3c311a2c4 100644 --- a/packages/InputDevices/res/values-sl/strings.xml +++ b/packages/InputDevices/res/values-sl/strings.xml @@ -36,6 +36,7 @@ <string name="keyboard_layout_slovak" msgid="2469379934672837296">"slovaška"</string> <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"slovenska"</string> <string name="keyboard_layout_turkish" msgid="7736163250907964898">"turška"</string> + <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Turščina F"</string> <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"ukrajinska"</string> <string name="keyboard_layout_arabic" msgid="5671970465174968712">"arabščina"</string> <string name="keyboard_layout_greek" msgid="7289253560162386040">"grščina"</string> diff --git a/packages/InputDevices/res/values-sq/strings.xml b/packages/InputDevices/res/values-sq/strings.xml index 9b4fe9ee7580..0863138423b4 100644 --- a/packages/InputDevices/res/values-sq/strings.xml +++ b/packages/InputDevices/res/values-sq/strings.xml @@ -36,6 +36,7 @@ <string name="keyboard_layout_slovak" msgid="2469379934672837296">"sllovakisht"</string> <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"sllovenisht"</string> <string name="keyboard_layout_turkish" msgid="7736163250907964898">"turqisht"</string> + <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Turqisht me F"</string> <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"ukrainisht"</string> <string name="keyboard_layout_arabic" msgid="5671970465174968712">"arabisht"</string> <string name="keyboard_layout_greek" msgid="7289253560162386040">"greqisht"</string> diff --git a/packages/InputDevices/res/values-sr/strings.xml b/packages/InputDevices/res/values-sr/strings.xml index e3a20438f34a..973b833e6a6a 100644 --- a/packages/InputDevices/res/values-sr/strings.xml +++ b/packages/InputDevices/res/values-sr/strings.xml @@ -36,6 +36,7 @@ <string name="keyboard_layout_slovak" msgid="2469379934672837296">"словачка"</string> <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"словеначка"</string> <string name="keyboard_layout_turkish" msgid="7736163250907964898">"турска"</string> + <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"турска F"</string> <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"украјинска"</string> <string name="keyboard_layout_arabic" msgid="5671970465174968712">"арапски"</string> <string name="keyboard_layout_greek" msgid="7289253560162386040">"грчки"</string> diff --git a/packages/InputDevices/res/values-sv/strings.xml b/packages/InputDevices/res/values-sv/strings.xml index 097ada40be63..a08a74b99786 100644 --- a/packages/InputDevices/res/values-sv/strings.xml +++ b/packages/InputDevices/res/values-sv/strings.xml @@ -36,6 +36,7 @@ <string name="keyboard_layout_slovak" msgid="2469379934672837296">"Slovakiskt"</string> <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Slovenskt"</string> <string name="keyboard_layout_turkish" msgid="7736163250907964898">"Turkiskt"</string> + <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"turkiska, F"</string> <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Ukrainskt"</string> <string name="keyboard_layout_arabic" msgid="5671970465174968712">"Arabiska"</string> <string name="keyboard_layout_greek" msgid="7289253560162386040">"Grekiska"</string> diff --git a/packages/InputDevices/res/values-sw/strings.xml b/packages/InputDevices/res/values-sw/strings.xml index 325796232d76..0f4c8466e47e 100644 --- a/packages/InputDevices/res/values-sw/strings.xml +++ b/packages/InputDevices/res/values-sw/strings.xml @@ -36,6 +36,7 @@ <string name="keyboard_layout_slovak" msgid="2469379934672837296">"Kislovakia"</string> <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Kislovenia"</string> <string name="keyboard_layout_turkish" msgid="7736163250907964898">"Kituruki"</string> + <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Kituruki F"</string> <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Kiukrania"</string> <string name="keyboard_layout_arabic" msgid="5671970465174968712">"Kiarabu"</string> <string name="keyboard_layout_greek" msgid="7289253560162386040">"Kigiriki"</string> diff --git a/packages/InputDevices/res/values-ta/strings.xml b/packages/InputDevices/res/values-ta/strings.xml index d3c6000fd9d4..f596b404c0f1 100644 --- a/packages/InputDevices/res/values-ta/strings.xml +++ b/packages/InputDevices/res/values-ta/strings.xml @@ -36,6 +36,7 @@ <string name="keyboard_layout_slovak" msgid="2469379934672837296">"ஸ்லோவாக்"</string> <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"ஸ்லோவேனியன்"</string> <string name="keyboard_layout_turkish" msgid="7736163250907964898">"டர்கிஷ்"</string> + <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"டர்கிஷ் F"</string> <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"உக்ரைனியன்"</string> <string name="keyboard_layout_arabic" msgid="5671970465174968712">"அரபிக்"</string> <string name="keyboard_layout_greek" msgid="7289253560162386040">"கிரேக்கம்"</string> diff --git a/packages/InputDevices/res/values-te/strings.xml b/packages/InputDevices/res/values-te/strings.xml index c0253e528684..5a10f6e03f71 100644 --- a/packages/InputDevices/res/values-te/strings.xml +++ b/packages/InputDevices/res/values-te/strings.xml @@ -36,6 +36,7 @@ <string name="keyboard_layout_slovak" msgid="2469379934672837296">"స్లోవక్"</string> <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"స్లోవేనియన్"</string> <string name="keyboard_layout_turkish" msgid="7736163250907964898">"టర్కిష్"</string> + <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"టర్కిష్ ఎఫ్"</string> <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"ఉక్రెయినియన్"</string> <string name="keyboard_layout_arabic" msgid="5671970465174968712">"అరబిక్"</string> <string name="keyboard_layout_greek" msgid="7289253560162386040">"గ్రీక్"</string> diff --git a/packages/InputDevices/res/values-th/strings.xml b/packages/InputDevices/res/values-th/strings.xml index c8e0e62f6e18..5f60c3d16b8c 100644 --- a/packages/InputDevices/res/values-th/strings.xml +++ b/packages/InputDevices/res/values-th/strings.xml @@ -36,6 +36,7 @@ <string name="keyboard_layout_slovak" msgid="2469379934672837296">"สโลวัก"</string> <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"สโลวีเนีย"</string> <string name="keyboard_layout_turkish" msgid="7736163250907964898">"ตุรกี"</string> + <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"ภาษาตุรกี F"</string> <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"ยูเครน"</string> <string name="keyboard_layout_arabic" msgid="5671970465174968712">"ภาษาอารบิค"</string> <string name="keyboard_layout_greek" msgid="7289253560162386040">"กรีก"</string> diff --git a/packages/InputDevices/res/values-tl/strings.xml b/packages/InputDevices/res/values-tl/strings.xml index b9aee76d400c..c42adbcbd785 100644 --- a/packages/InputDevices/res/values-tl/strings.xml +++ b/packages/InputDevices/res/values-tl/strings.xml @@ -36,6 +36,7 @@ <string name="keyboard_layout_slovak" msgid="2469379934672837296">"Slovak"</string> <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Slovenian"</string> <string name="keyboard_layout_turkish" msgid="7736163250907964898">"Turkish"</string> + <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Turkish F"</string> <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Ukrainian"</string> <string name="keyboard_layout_arabic" msgid="5671970465174968712">"Arabic"</string> <string name="keyboard_layout_greek" msgid="7289253560162386040">"Greek"</string> diff --git a/packages/InputDevices/res/values-tr/strings.xml b/packages/InputDevices/res/values-tr/strings.xml index f093abb9a708..2877cb7e011f 100644 --- a/packages/InputDevices/res/values-tr/strings.xml +++ b/packages/InputDevices/res/values-tr/strings.xml @@ -36,6 +36,7 @@ <string name="keyboard_layout_slovak" msgid="2469379934672837296">"Slovakça"</string> <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Slovence"</string> <string name="keyboard_layout_turkish" msgid="7736163250907964898">"Türkçe"</string> + <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Türkçe F"</string> <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Ukraynaca"</string> <string name="keyboard_layout_arabic" msgid="5671970465174968712">"Arapça"</string> <string name="keyboard_layout_greek" msgid="7289253560162386040">"Yunanca"</string> diff --git a/packages/InputDevices/res/values-uk/strings.xml b/packages/InputDevices/res/values-uk/strings.xml index d9b58d21c14f..3b0de3463b84 100644 --- a/packages/InputDevices/res/values-uk/strings.xml +++ b/packages/InputDevices/res/values-uk/strings.xml @@ -36,6 +36,7 @@ <string name="keyboard_layout_slovak" msgid="2469379934672837296">"словацька"</string> <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"словенська"</string> <string name="keyboard_layout_turkish" msgid="7736163250907964898">"турецька"</string> + <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Турецька-F"</string> <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"українська"</string> <string name="keyboard_layout_arabic" msgid="5671970465174968712">"Арабська"</string> <string name="keyboard_layout_greek" msgid="7289253560162386040">"Грецька"</string> diff --git a/packages/InputDevices/res/values-ur/strings.xml b/packages/InputDevices/res/values-ur/strings.xml index 2bff7ed2f234..0cc9b61ed5ec 100644 --- a/packages/InputDevices/res/values-ur/strings.xml +++ b/packages/InputDevices/res/values-ur/strings.xml @@ -36,6 +36,7 @@ <string name="keyboard_layout_slovak" msgid="2469379934672837296">"سلوووک"</string> <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"سلووینیائی"</string> <string name="keyboard_layout_turkish" msgid="7736163250907964898">"ترکش"</string> + <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"ترکی-F"</string> <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"يُوکرينی"</string> <string name="keyboard_layout_arabic" msgid="5671970465174968712">"عربی"</string> <string name="keyboard_layout_greek" msgid="7289253560162386040">"يونانی"</string> diff --git a/packages/InputDevices/res/values-uz/strings.xml b/packages/InputDevices/res/values-uz/strings.xml index 9245aebd22ba..161bd0d15806 100644 --- a/packages/InputDevices/res/values-uz/strings.xml +++ b/packages/InputDevices/res/values-uz/strings.xml @@ -36,6 +36,7 @@ <string name="keyboard_layout_slovak" msgid="2469379934672837296">"Slovakcha"</string> <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Slovyancha"</string> <string name="keyboard_layout_turkish" msgid="7736163250907964898">"Turkcha"</string> + <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Turkcha F"</string> <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Ukraincha"</string> <string name="keyboard_layout_arabic" msgid="5671970465174968712">"Arab"</string> <string name="keyboard_layout_greek" msgid="7289253560162386040">"Grek"</string> diff --git a/packages/InputDevices/res/values-vi/strings.xml b/packages/InputDevices/res/values-vi/strings.xml index 76fd0bff0e2a..0c638faaecd0 100644 --- a/packages/InputDevices/res/values-vi/strings.xml +++ b/packages/InputDevices/res/values-vi/strings.xml @@ -36,6 +36,7 @@ <string name="keyboard_layout_slovak" msgid="2469379934672837296">"Tiếng Slovak"</string> <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Tiếng Sloven"</string> <string name="keyboard_layout_turkish" msgid="7736163250907964898">"Tiếng Thổ Nhĩ Kỳ"</string> + <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"Tiếng Thổ Nhĩ Kỳ F"</string> <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Tiếng Ukraina"</string> <string name="keyboard_layout_arabic" msgid="5671970465174968712">"Tiếng Ả rập"</string> <string name="keyboard_layout_greek" msgid="7289253560162386040">"Tiếng Hy Lạp"</string> diff --git a/packages/InputDevices/res/values-zh-rCN/strings.xml b/packages/InputDevices/res/values-zh-rCN/strings.xml index aa75605b5abd..b24977982bce 100644 --- a/packages/InputDevices/res/values-zh-rCN/strings.xml +++ b/packages/InputDevices/res/values-zh-rCN/strings.xml @@ -36,6 +36,7 @@ <string name="keyboard_layout_slovak" msgid="2469379934672837296">"斯洛伐克语"</string> <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"斯洛文尼亚语"</string> <string name="keyboard_layout_turkish" msgid="7736163250907964898">"土耳其语"</string> + <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"土耳其语 F 型键盘"</string> <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"乌克兰语"</string> <string name="keyboard_layout_arabic" msgid="5671970465174968712">"阿拉伯语"</string> <string name="keyboard_layout_greek" msgid="7289253560162386040">"希腊语"</string> diff --git a/packages/InputDevices/res/values-zh-rHK/strings.xml b/packages/InputDevices/res/values-zh-rHK/strings.xml index dc824b97369d..60a52e98ae98 100644 --- a/packages/InputDevices/res/values-zh-rHK/strings.xml +++ b/packages/InputDevices/res/values-zh-rHK/strings.xml @@ -36,6 +36,7 @@ <string name="keyboard_layout_slovak" msgid="2469379934672837296">"斯洛伐克文"</string> <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"斯洛文尼亞文"</string> <string name="keyboard_layout_turkish" msgid="7736163250907964898">"土耳其文"</string> + <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"土耳其文 F"</string> <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"烏克蘭文"</string> <string name="keyboard_layout_arabic" msgid="5671970465174968712">"阿拉伯文"</string> <string name="keyboard_layout_greek" msgid="7289253560162386040">"希臘文"</string> diff --git a/packages/InputDevices/res/values-zh-rTW/strings.xml b/packages/InputDevices/res/values-zh-rTW/strings.xml index c2714da0c3d9..c3217e368492 100644 --- a/packages/InputDevices/res/values-zh-rTW/strings.xml +++ b/packages/InputDevices/res/values-zh-rTW/strings.xml @@ -36,6 +36,7 @@ <string name="keyboard_layout_slovak" msgid="2469379934672837296">"斯洛伐克文"</string> <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"斯洛維尼亞文"</string> <string name="keyboard_layout_turkish" msgid="7736163250907964898">"土耳其文"</string> + <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"土耳其文 F"</string> <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"烏克蘭文"</string> <string name="keyboard_layout_arabic" msgid="5671970465174968712">"阿拉伯文"</string> <string name="keyboard_layout_greek" msgid="7289253560162386040">"希臘文"</string> diff --git a/packages/InputDevices/res/values-zu/strings.xml b/packages/InputDevices/res/values-zu/strings.xml index 3af1da1ac694..2c53626e55f8 100644 --- a/packages/InputDevices/res/values-zu/strings.xml +++ b/packages/InputDevices/res/values-zu/strings.xml @@ -36,6 +36,7 @@ <string name="keyboard_layout_slovak" msgid="2469379934672837296">"Isi-Slovak"</string> <string name="keyboard_layout_slovenian" msgid="1735933028924982368">"Isi-Slovenian"</string> <string name="keyboard_layout_turkish" msgid="7736163250907964898">"Isi-Turkish"</string> + <string name="keyboard_layout_turkish_f" msgid="9130320856010776018">"I-Turkish-F"</string> <string name="keyboard_layout_ukrainian" msgid="8176637744389480417">"Isi-Ukrainian"</string> <string name="keyboard_layout_arabic" msgid="5671970465174968712">"Isi-Arabic"</string> <string name="keyboard_layout_greek" msgid="7289253560162386040">"Isi-Greek"</string> diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/FusedPrintersProvider.java b/packages/PrintSpooler/src/com/android/printspooler/ui/FusedPrintersProvider.java index 8e140ca27971..83974afd083f 100644 --- a/packages/PrintSpooler/src/com/android/printspooler/ui/FusedPrintersProvider.java +++ b/packages/PrintSpooler/src/com/android/printspooler/ui/FusedPrintersProvider.java @@ -254,7 +254,7 @@ public final class FusedPrintersProvider extends Loader<List<PrinterInfo>> mLocationManager.requestLocationUpdates( LocationManager.FUSED_PROVIDER, new LocationRequest.Builder(LOCATION_UPDATE_MS) - .setQuality(LocationRequest.POWER_LOW) + .setQuality(LocationRequest.QUALITY_LOW_POWER) .build(), new HandlerExecutor(new Handler(Looper.getMainLooper())), this); diff --git a/packages/SettingsLib/res/values-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml index 2f97f272c8b8..e2b04d485488 100644 --- a/packages/SettingsLib/res/values-af/strings.xml +++ b/packages/SettingsLib/res/values-af/strings.xml @@ -545,19 +545,14 @@ <string name="user_need_lock_message" msgid="4311424336209509301">"Voordat jy \'n beperkte profiel kan skep, moet jy \'n skermslot opstel om jou programme en persoonlike data te beskerm."</string> <string name="user_set_lock_button" msgid="1427128184982594856">"Stel slot op"</string> <string name="user_switch_to_user" msgid="6975428297154968543">"Skakel oor na <xliff:g id="USER_NAME">%s</xliff:g>"</string> - <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) --> - <skip /> - <!-- no translation found for user_nickname (262624187455825083) --> - <skip /> + <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Skep tans nuwe gebruiker …"</string> + <string name="user_nickname" msgid="262624187455825083">"Bynaam"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Voeg gas by"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Verwyder gas"</string> <string name="guest_nickname" msgid="6332276931583337261">"Gas"</string> - <!-- no translation found for user_image_take_photo (467512954561638530) --> - <skip /> - <!-- no translation found for user_image_choose_photo (1363820919146782908) --> - <skip /> - <!-- no translation found for user_image_photo_selector (433658323306627093) --> - <skip /> + <string name="user_image_take_photo" msgid="467512954561638530">"Neem \'n foto"</string> + <string name="user_image_choose_photo" msgid="1363820919146782908">"Kies \'n prent"</string> + <string name="user_image_photo_selector" msgid="433658323306627093">"Kies foto"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Toestelverstek"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Gedeaktiveer"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Geaktiveer"</string> diff --git a/packages/SettingsLib/res/values-am/strings.xml b/packages/SettingsLib/res/values-am/strings.xml index f0d8a40b54ef..0e3684e27527 100644 --- a/packages/SettingsLib/res/values-am/strings.xml +++ b/packages/SettingsLib/res/values-am/strings.xml @@ -545,19 +545,14 @@ <string name="user_need_lock_message" msgid="4311424336209509301">"የተገደበ መገለጫ መፍጠር ከመቻልዎ በፊት መተግበሪያዎችዎን እና የግል ውሂብዎን ለመጠበቅ ቁልፍ ማያ ገጽ ማዋቀር አለብዎት።"</string> <string name="user_set_lock_button" msgid="1427128184982594856">"ቁልፍ አዘጋጅ"</string> <string name="user_switch_to_user" msgid="6975428297154968543">"ወደ <xliff:g id="USER_NAME">%s</xliff:g> ቀይር"</string> - <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) --> - <skip /> - <!-- no translation found for user_nickname (262624187455825083) --> - <skip /> + <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"አዲስ ተጠቃሚ በመፍጠር ላይ…"</string> + <string name="user_nickname" msgid="262624187455825083">"ቅጽል ስም"</string> <string name="guest_new_guest" msgid="3482026122932643557">"እንግዳን አክል"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"እንግዳን አስወግድ"</string> <string name="guest_nickname" msgid="6332276931583337261">"እንግዳ"</string> - <!-- no translation found for user_image_take_photo (467512954561638530) --> - <skip /> - <!-- no translation found for user_image_choose_photo (1363820919146782908) --> - <skip /> - <!-- no translation found for user_image_photo_selector (433658323306627093) --> - <skip /> + <string name="user_image_take_photo" msgid="467512954561638530">"ፎቶ አንሳ"</string> + <string name="user_image_choose_photo" msgid="1363820919146782908">"ምስል ይምረጡ"</string> + <string name="user_image_photo_selector" msgid="433658323306627093">"ፎቶ ይምረጡ"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"የመሣሪያ ነባሪ"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"ተሰናክሏል"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"ነቅቷል"</string> diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml index 323ba44ba420..7ec46d223d11 100644 --- a/packages/SettingsLib/res/values-ar/strings.xml +++ b/packages/SettingsLib/res/values-ar/strings.xml @@ -549,19 +549,14 @@ <string name="user_need_lock_message" msgid="4311424336209509301">"قبل أن تتمكن من إنشاء ملف شخصي مقيد، يلزمك إعداد تأمين للشاشة لحماية تطبيقاتك وبياناتك الشخصية."</string> <string name="user_set_lock_button" msgid="1427128184982594856">"تعيين التأمين"</string> <string name="user_switch_to_user" msgid="6975428297154968543">"التبديل إلى <xliff:g id="USER_NAME">%s</xliff:g>"</string> - <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) --> - <skip /> - <!-- no translation found for user_nickname (262624187455825083) --> - <skip /> + <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"جارٍ إنشاء مستخدم جديد…"</string> + <string name="user_nickname" msgid="262624187455825083">"اللقب"</string> <string name="guest_new_guest" msgid="3482026122932643557">"إضافة ضيف"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"إزالة جلسة الضيف"</string> <string name="guest_nickname" msgid="6332276931583337261">"ضيف"</string> - <!-- no translation found for user_image_take_photo (467512954561638530) --> - <skip /> - <!-- no translation found for user_image_choose_photo (1363820919146782908) --> - <skip /> - <!-- no translation found for user_image_photo_selector (433658323306627093) --> - <skip /> + <string name="user_image_take_photo" msgid="467512954561638530">"التقاط صورة"</string> + <string name="user_image_choose_photo" msgid="1363820919146782908">"اختيار صورة"</string> + <string name="user_image_photo_selector" msgid="433658323306627093">"اختيار صورة"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"الإعداد التلقائي للجهاز"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"غير مفعّل"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"مفعّل"</string> diff --git a/packages/SettingsLib/res/values-as/strings.xml b/packages/SettingsLib/res/values-as/strings.xml index 7679c649c350..fe0409451fb5 100644 --- a/packages/SettingsLib/res/values-as/strings.xml +++ b/packages/SettingsLib/res/values-as/strings.xml @@ -545,19 +545,14 @@ <string name="user_need_lock_message" msgid="4311424336209509301">"আপুনি সীমিত প্ৰ\'ফাইল এটা সৃষ্টি কৰাৰ আগেয়ে, আপোনাৰ ব্যক্তিগত ডেটা আৰু এপবিলাকক সুৰক্ষিত কৰিবলৈ স্ক্ৰীণ লক এটা নিৰ্ধাৰণ কৰিব লাগিব।"</string> <string name="user_set_lock_button" msgid="1427128184982594856">"লক ছেট কৰক"</string> <string name="user_switch_to_user" msgid="6975428297154968543">"<xliff:g id="USER_NAME">%s</xliff:g>লৈ সলনি কৰক"</string> - <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) --> - <skip /> - <!-- no translation found for user_nickname (262624187455825083) --> - <skip /> + <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"নতুন ব্যৱহাৰকাৰী সৃষ্টি কৰি থকা হৈছে…"</string> + <string name="user_nickname" msgid="262624187455825083">"উপনাম"</string> <string name="guest_new_guest" msgid="3482026122932643557">"অতিথি যোগ কৰক"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"অতিথি আঁতৰাওক"</string> <string name="guest_nickname" msgid="6332276931583337261">"অতিথি"</string> - <!-- no translation found for user_image_take_photo (467512954561638530) --> - <skip /> - <!-- no translation found for user_image_choose_photo (1363820919146782908) --> - <skip /> - <!-- no translation found for user_image_photo_selector (433658323306627093) --> - <skip /> + <string name="user_image_take_photo" msgid="467512954561638530">"এখন ফট’ তোলক"</string> + <string name="user_image_choose_photo" msgid="1363820919146782908">"এখন প্ৰতিচ্ছবি বাছনি কৰক"</string> + <string name="user_image_photo_selector" msgid="433658323306627093">"ফট’ বাছনি কৰক"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"ডিভাইচ ডিফ’ল্ট"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"অক্ষম কৰা আছে"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"সক্ষম কৰা আছে"</string> diff --git a/packages/SettingsLib/res/values-az/strings.xml b/packages/SettingsLib/res/values-az/strings.xml index d0bf0edca17a..bd10cfb928ac 100644 --- a/packages/SettingsLib/res/values-az/strings.xml +++ b/packages/SettingsLib/res/values-az/strings.xml @@ -545,19 +545,14 @@ <string name="user_need_lock_message" msgid="4311424336209509301">"Məhdudlaşdırılmış profil yaratmadan öncə, Siz tətbiqlərinizi və şəxsi datanızı qorumaq üçün ekran kilidi quraşdırmalısınız."</string> <string name="user_set_lock_button" msgid="1427128184982594856">"Kilid ayarlayın"</string> <string name="user_switch_to_user" msgid="6975428297154968543">"<xliff:g id="USER_NAME">%s</xliff:g> adlı istifadəçiyə keçin"</string> - <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) --> - <skip /> - <!-- no translation found for user_nickname (262624187455825083) --> - <skip /> + <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Yeni istifadəçi yaradılır…"</string> + <string name="user_nickname" msgid="262624187455825083">"Ləqəb"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Qonaq əlavə edin"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Qonağı silin"</string> <string name="guest_nickname" msgid="6332276931583337261">"Qonaq"</string> - <!-- no translation found for user_image_take_photo (467512954561638530) --> - <skip /> - <!-- no translation found for user_image_choose_photo (1363820919146782908) --> - <skip /> - <!-- no translation found for user_image_photo_selector (433658323306627093) --> - <skip /> + <string name="user_image_take_photo" msgid="467512954561638530">"Foto çəkin"</string> + <string name="user_image_choose_photo" msgid="1363820919146782908">"Şəkil seçin"</string> + <string name="user_image_photo_selector" msgid="433658323306627093">"Foto seçin"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Cihaz defoltu"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Deaktiv"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Aktiv"</string> diff --git a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml index 3beb9b84e383..0deb9273c63b 100644 --- a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml +++ b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml @@ -546,19 +546,14 @@ <string name="user_need_lock_message" msgid="4311424336209509301">"Da biste mogli da napravite ograničeni profil, treba da podesite zaključavanje ekrana da biste zaštitili aplikacije i lične podatke."</string> <string name="user_set_lock_button" msgid="1427128184982594856">"Podesi zaključavanje"</string> <string name="user_switch_to_user" msgid="6975428297154968543">"Pređi na korisnika <xliff:g id="USER_NAME">%s</xliff:g>"</string> - <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) --> - <skip /> - <!-- no translation found for user_nickname (262624187455825083) --> - <skip /> + <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Pravi se novi korisnik…"</string> + <string name="user_nickname" msgid="262624187455825083">"Nadimak"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Dodaj gosta"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Ukloni gosta"</string> <string name="guest_nickname" msgid="6332276931583337261">"Gost"</string> - <!-- no translation found for user_image_take_photo (467512954561638530) --> - <skip /> - <!-- no translation found for user_image_choose_photo (1363820919146782908) --> - <skip /> - <!-- no translation found for user_image_photo_selector (433658323306627093) --> - <skip /> + <string name="user_image_take_photo" msgid="467512954561638530">"Slikaj"</string> + <string name="user_image_choose_photo" msgid="1363820919146782908">"Odaberi sliku"</string> + <string name="user_image_photo_selector" msgid="433658323306627093">"Izaberite sliku"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Podrazumevano za uređaj"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Onemogućeno"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Omogućeno"</string> diff --git a/packages/SettingsLib/res/values-be/strings.xml b/packages/SettingsLib/res/values-be/strings.xml index af6da4e64a36..9aad825d91ae 100644 --- a/packages/SettingsLib/res/values-be/strings.xml +++ b/packages/SettingsLib/res/values-be/strings.xml @@ -547,19 +547,14 @@ <string name="user_need_lock_message" msgid="4311424336209509301">"Перш чым вы зможаце стварыць профіль з абмежаваннямi, вам трэба наладзіць блакiроўку экрана для абароны сваiх дадаткаў і асабістай інфармацыі."</string> <string name="user_set_lock_button" msgid="1427128184982594856">"Усталёўка блакiроўкi"</string> <string name="user_switch_to_user" msgid="6975428297154968543">"Пераключыцца на карыстальніка <xliff:g id="USER_NAME">%s</xliff:g>"</string> - <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) --> - <skip /> - <!-- no translation found for user_nickname (262624187455825083) --> - <skip /> + <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Ствараецца новы карыстальнік…"</string> + <string name="user_nickname" msgid="262624187455825083">"Псеўданім"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Дадаць госця"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Выдаліць госця"</string> <string name="guest_nickname" msgid="6332276931583337261">"Госць"</string> - <!-- no translation found for user_image_take_photo (467512954561638530) --> - <skip /> - <!-- no translation found for user_image_choose_photo (1363820919146782908) --> - <skip /> - <!-- no translation found for user_image_photo_selector (433658323306627093) --> - <skip /> + <string name="user_image_take_photo" msgid="467512954561638530">"Зрабіць фота"</string> + <string name="user_image_choose_photo" msgid="1363820919146782908">"Выбраць відарыс"</string> + <string name="user_image_photo_selector" msgid="433658323306627093">"Выбраць фота"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Стандартная прылада"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Выключана"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Уключана"</string> diff --git a/packages/SettingsLib/res/values-bg/strings.xml b/packages/SettingsLib/res/values-bg/strings.xml index 0097c9ced9ad..8bf13fe56c05 100644 --- a/packages/SettingsLib/res/values-bg/strings.xml +++ b/packages/SettingsLib/res/values-bg/strings.xml @@ -545,19 +545,14 @@ <string name="user_need_lock_message" msgid="4311424336209509301">"Преди да можете да създадете потребителски профил с ограничена функционалност, трябва да настроите заключения екран, за да защитите приложенията и личните си данни."</string> <string name="user_set_lock_button" msgid="1427128184982594856">"Задаване на заключване"</string> <string name="user_switch_to_user" msgid="6975428297154968543">"Превключване към <xliff:g id="USER_NAME">%s</xliff:g>"</string> - <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) --> - <skip /> - <!-- no translation found for user_nickname (262624187455825083) --> - <skip /> + <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Създава се нов потребител…"</string> + <string name="user_nickname" msgid="262624187455825083">"Псевдоним"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Добавяне на гост"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Премахване на госта"</string> <string name="guest_nickname" msgid="6332276931583337261">"Гост"</string> - <!-- no translation found for user_image_take_photo (467512954561638530) --> - <skip /> - <!-- no translation found for user_image_choose_photo (1363820919146782908) --> - <skip /> - <!-- no translation found for user_image_photo_selector (433658323306627093) --> - <skip /> + <string name="user_image_take_photo" msgid="467512954561638530">"Правене на снимка"</string> + <string name="user_image_choose_photo" msgid="1363820919146782908">"Избиране на изображение"</string> + <string name="user_image_photo_selector" msgid="433658323306627093">"Избиране на снимката"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Стандартна настройка за у-вото"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Деактивирано"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Активирано"</string> diff --git a/packages/SettingsLib/res/values-bn/strings.xml b/packages/SettingsLib/res/values-bn/strings.xml index c3515420efed..8bcf9a6ba19f 100644 --- a/packages/SettingsLib/res/values-bn/strings.xml +++ b/packages/SettingsLib/res/values-bn/strings.xml @@ -545,19 +545,14 @@ <string name="user_need_lock_message" msgid="4311424336209509301">"আপনি একটি সীমাবদ্ধযুক্ত প্রোফাইল তৈরি করার আগে, আপনাকে আপনার অ্যাপ্লিকেশন এবং ব্যক্তিগত ডেটা সুরক্ষিত করার জন্য একটি স্ক্রিন লক সেট-আপ করতে হবে।"</string> <string name="user_set_lock_button" msgid="1427128184982594856">"লক সেট করুন"</string> <string name="user_switch_to_user" msgid="6975428297154968543">"<xliff:g id="USER_NAME">%s</xliff:g>-এ পাল্টান"</string> - <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) --> - <skip /> - <!-- no translation found for user_nickname (262624187455825083) --> - <skip /> + <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"নতুন ব্যবহারকারী তৈরি করা হচ্ছে…"</string> + <string name="user_nickname" msgid="262624187455825083">"বিশেষ নাম"</string> <string name="guest_new_guest" msgid="3482026122932643557">"অতিথি যোগ করুন"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"অতিথি সরান"</string> <string name="guest_nickname" msgid="6332276931583337261">"অতিথি"</string> - <!-- no translation found for user_image_take_photo (467512954561638530) --> - <skip /> - <!-- no translation found for user_image_choose_photo (1363820919146782908) --> - <skip /> - <!-- no translation found for user_image_photo_selector (433658323306627093) --> - <skip /> + <string name="user_image_take_photo" msgid="467512954561638530">"ফটো তুলুন"</string> + <string name="user_image_choose_photo" msgid="1363820919146782908">"একটি ইমেজ বেছে নিন"</string> + <string name="user_image_photo_selector" msgid="433658323306627093">"ফটো বেছে নিন"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"ডিভাইসের ডিফল্ট"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"বন্ধ করা আছে"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"চালু করা আছে"</string> diff --git a/packages/SettingsLib/res/values-bs/arrays.xml b/packages/SettingsLib/res/values-bs/arrays.xml index 775cd15edc0b..6d2f1f36f233 100644 --- a/packages/SettingsLib/res/values-bs/arrays.xml +++ b/packages/SettingsLib/res/values-bs/arrays.xml @@ -27,7 +27,7 @@ <item msgid="8356618438494652335">"Autentifikacija…"</item> <item msgid="2837871868181677206">"Dobivanje IP adrese…"</item> <item msgid="4613015005934755724">"Povezano"</item> - <item msgid="3763530049995655072">"Suspendirano"</item> + <item msgid="3763530049995655072">"Obustavljeno"</item> <item msgid="7852381437933824454">"Prekidanje veze…"</item> <item msgid="5046795712175415059">"Isključen"</item> <item msgid="2473654476624070462">"Neuspješno"</item> @@ -41,7 +41,7 @@ <item msgid="3028983857109369308">"Autentifikacija s mrežom <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item> <item msgid="4287401332778341890">"Dobivanje IP adrese iz mreže <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item> <item msgid="1043944043827424501">"Povezano s mrežom <xliff:g id="NETWORK_NAME">%1$s</xliff:g>"</item> - <item msgid="7445993821842009653">"Suspendirano"</item> + <item msgid="7445993821842009653">"Obustavljeno"</item> <item msgid="1175040558087735707">"Prekidanje veze s mrežom <xliff:g id="NETWORK_NAME">%1$s</xliff:g>…"</item> <item msgid="699832486578171722">"Isključen"</item> <item msgid="522383512264986901">"Neuspješno"</item> diff --git a/packages/SettingsLib/res/values-bs/strings.xml b/packages/SettingsLib/res/values-bs/strings.xml index 3d3587302e67..32f6aa7774a7 100644 --- a/packages/SettingsLib/res/values-bs/strings.xml +++ b/packages/SettingsLib/res/values-bs/strings.xml @@ -546,19 +546,14 @@ <string name="user_need_lock_message" msgid="4311424336209509301">"Prije nego vam se omogući kreiranje ograničenog profila, morate postaviti zaključavanje ekrana da biste zaštitili svoje aplikacije i lične podatke."</string> <string name="user_set_lock_button" msgid="1427128184982594856">"Postaviti zaključavanje"</string> <string name="user_switch_to_user" msgid="6975428297154968543">"Prebaci na korisnika <xliff:g id="USER_NAME">%s</xliff:g>"</string> - <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) --> - <skip /> - <!-- no translation found for user_nickname (262624187455825083) --> - <skip /> + <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Kreiranje novog korisnika…"</string> + <string name="user_nickname" msgid="262624187455825083">"Nadimak"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Dodaj gosta"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Ukloni gosta"</string> <string name="guest_nickname" msgid="6332276931583337261">"Gost"</string> - <!-- no translation found for user_image_take_photo (467512954561638530) --> - <skip /> - <!-- no translation found for user_image_choose_photo (1363820919146782908) --> - <skip /> - <!-- no translation found for user_image_photo_selector (433658323306627093) --> - <skip /> + <string name="user_image_take_photo" msgid="467512954561638530">"Snimite fotografiju"</string> + <string name="user_image_choose_photo" msgid="1363820919146782908">"Odaberite sliku"</string> + <string name="user_image_photo_selector" msgid="433658323306627093">"Odabir fotografije"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Zadana postavka uređaja"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Onemogućeno"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Omogućeno"</string> diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml index b1edd7877650..525a1878c45c 100644 --- a/packages/SettingsLib/res/values-ca/strings.xml +++ b/packages/SettingsLib/res/values-ca/strings.xml @@ -545,19 +545,14 @@ <string name="user_need_lock_message" msgid="4311424336209509301">"Per crear un perfil restringit, has de configurar una pantalla de bloqueig per protegir les aplicacions i les dades personals."</string> <string name="user_set_lock_button" msgid="1427128184982594856">"Defineix un bloqueig"</string> <string name="user_switch_to_user" msgid="6975428297154968543">"Canvia a <xliff:g id="USER_NAME">%s</xliff:g>"</string> - <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) --> - <skip /> - <!-- no translation found for user_nickname (262624187455825083) --> - <skip /> + <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"S\'està creant l\'usuari…"</string> + <string name="user_nickname" msgid="262624187455825083">"Àlies"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Afegeix un convidat"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Suprimeix el convidat"</string> <string name="guest_nickname" msgid="6332276931583337261">"Convidat"</string> - <!-- no translation found for user_image_take_photo (467512954561638530) --> - <skip /> - <!-- no translation found for user_image_choose_photo (1363820919146782908) --> - <skip /> - <!-- no translation found for user_image_photo_selector (433658323306627093) --> - <skip /> + <string name="user_image_take_photo" msgid="467512954561638530">"Fes una foto"</string> + <string name="user_image_choose_photo" msgid="1363820919146782908">"Tria una imatge"</string> + <string name="user_image_photo_selector" msgid="433658323306627093">"Selecciona una foto"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Opció predeter. del dispositiu"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Desactivat"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Activat"</string> diff --git a/packages/SettingsLib/res/values-cs/strings.xml b/packages/SettingsLib/res/values-cs/strings.xml index bdce444ce81b..64ad700c65bc 100644 --- a/packages/SettingsLib/res/values-cs/strings.xml +++ b/packages/SettingsLib/res/values-cs/strings.xml @@ -547,19 +547,14 @@ <string name="user_need_lock_message" msgid="4311424336209509301">"Před vytvořením omezeného profilu je nutné nejprve nastavit zámek obrazovky k ochraně aplikací a dat."</string> <string name="user_set_lock_button" msgid="1427128184982594856">"Nastavit zámek"</string> <string name="user_switch_to_user" msgid="6975428297154968543">"Přepnout na uživatele <xliff:g id="USER_NAME">%s</xliff:g>"</string> - <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) --> - <skip /> - <!-- no translation found for user_nickname (262624187455825083) --> - <skip /> + <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Vytváření nového uživatele…"</string> + <string name="user_nickname" msgid="262624187455825083">"Přezdívka"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Přidat hosta"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Odstranit hosta"</string> <string name="guest_nickname" msgid="6332276931583337261">"Host"</string> - <!-- no translation found for user_image_take_photo (467512954561638530) --> - <skip /> - <!-- no translation found for user_image_choose_photo (1363820919146782908) --> - <skip /> - <!-- no translation found for user_image_photo_selector (433658323306627093) --> - <skip /> + <string name="user_image_take_photo" msgid="467512954561638530">"Pořídit fotku"</string> + <string name="user_image_choose_photo" msgid="1363820919146782908">"Vybrat obrázek"</string> + <string name="user_image_photo_selector" msgid="433658323306627093">"Vybrat fotku"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Výchozí nastavení zařízení"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Vypnuto"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Zapnuto"</string> diff --git a/packages/SettingsLib/res/values-da/strings.xml b/packages/SettingsLib/res/values-da/strings.xml index 1dc0e4ed077f..433368bb9a83 100644 --- a/packages/SettingsLib/res/values-da/strings.xml +++ b/packages/SettingsLib/res/values-da/strings.xml @@ -545,19 +545,14 @@ <string name="user_need_lock_message" msgid="4311424336209509301">"Før du kan oprette en begrænset profil, skal du oprette en skærmlås for at beskytte dine apps og personlige data."</string> <string name="user_set_lock_button" msgid="1427128184982594856">"Konfigurer låseskærmen"</string> <string name="user_switch_to_user" msgid="6975428297154968543">"Skift til <xliff:g id="USER_NAME">%s</xliff:g>"</string> - <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) --> - <skip /> - <!-- no translation found for user_nickname (262624187455825083) --> - <skip /> + <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Opretter ny bruger…"</string> + <string name="user_nickname" msgid="262624187455825083">"Kaldenavn"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Tilføj gæsten"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Fjern gæsten"</string> <string name="guest_nickname" msgid="6332276931583337261">"Gæst"</string> - <!-- no translation found for user_image_take_photo (467512954561638530) --> - <skip /> - <!-- no translation found for user_image_choose_photo (1363820919146782908) --> - <skip /> - <!-- no translation found for user_image_photo_selector (433658323306627093) --> - <skip /> + <string name="user_image_take_photo" msgid="467512954561638530">"Tag et billede"</string> + <string name="user_image_choose_photo" msgid="1363820919146782908">"Vælg et billede"</string> + <string name="user_image_photo_selector" msgid="433658323306627093">"Vælg billede"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Enhedens standardindstilling"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Deaktiveret"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Aktiveret"</string> diff --git a/packages/SettingsLib/res/values-de/strings.xml b/packages/SettingsLib/res/values-de/strings.xml index a9cdf1ff81bf..9946eed222e2 100644 --- a/packages/SettingsLib/res/values-de/strings.xml +++ b/packages/SettingsLib/res/values-de/strings.xml @@ -545,19 +545,14 @@ <string name="user_need_lock_message" msgid="4311424336209509301">"Vor dem Erstellen eines eingeschränkten Profils musst du eine Displaysperre einrichten, um deine Apps und personenbezogenen Daten zu schützen."</string> <string name="user_set_lock_button" msgid="1427128184982594856">"Sperre einrichten"</string> <string name="user_switch_to_user" msgid="6975428297154968543">"Zu <xliff:g id="USER_NAME">%s</xliff:g> wechseln"</string> - <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) --> - <skip /> - <!-- no translation found for user_nickname (262624187455825083) --> - <skip /> + <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Neuer Nutzer wird erstellt…"</string> + <string name="user_nickname" msgid="262624187455825083">"Alias"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Gast hinzufügen"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Gast entfernen"</string> <string name="guest_nickname" msgid="6332276931583337261">"Gast"</string> - <!-- no translation found for user_image_take_photo (467512954561638530) --> - <skip /> - <!-- no translation found for user_image_choose_photo (1363820919146782908) --> - <skip /> - <!-- no translation found for user_image_photo_selector (433658323306627093) --> - <skip /> + <string name="user_image_take_photo" msgid="467512954561638530">"Foto machen"</string> + <string name="user_image_choose_photo" msgid="1363820919146782908">"Bild auswählen"</string> + <string name="user_image_photo_selector" msgid="433658323306627093">"Foto auswählen"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Gerätestandard"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Deaktiviert"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Aktiviert"</string> diff --git a/packages/SettingsLib/res/values-el/strings.xml b/packages/SettingsLib/res/values-el/strings.xml index 00b843495a9b..c265dbc427ec 100644 --- a/packages/SettingsLib/res/values-el/strings.xml +++ b/packages/SettingsLib/res/values-el/strings.xml @@ -545,19 +545,14 @@ <string name="user_need_lock_message" msgid="4311424336209509301">"Προκειμένου να μπορέσετε να δημιουργήσετε ένα περιορισμένο προφίλ, θα πρέπει να δημιουργήσετε ένα κλείδωμα οθόνης για την προστασία των εφαρμογών και των προσωπικών δεδομένων σας."</string> <string name="user_set_lock_button" msgid="1427128184982594856">"Ορισμός κλειδώματος"</string> <string name="user_switch_to_user" msgid="6975428297154968543">"Εναλλαγή σε <xliff:g id="USER_NAME">%s</xliff:g>"</string> - <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) --> - <skip /> - <!-- no translation found for user_nickname (262624187455825083) --> - <skip /> + <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Δημιουργία νέου χρήστη…"</string> + <string name="user_nickname" msgid="262624187455825083">"Ψευδώνυμο"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Προσθήκη επισκέπτη"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Κατάργηση επισκέπτη"</string> <string name="guest_nickname" msgid="6332276931583337261">"Επισκέπτης"</string> - <!-- no translation found for user_image_take_photo (467512954561638530) --> - <skip /> - <!-- no translation found for user_image_choose_photo (1363820919146782908) --> - <skip /> - <!-- no translation found for user_image_photo_selector (433658323306627093) --> - <skip /> + <string name="user_image_take_photo" msgid="467512954561638530">"Λήψη φωτογραφίας"</string> + <string name="user_image_choose_photo" msgid="1363820919146782908">"Επιλογή εικόνας"</string> + <string name="user_image_photo_selector" msgid="433658323306627093">"Επιλογή φωτογραφίας"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Προεπιλογή συσκευής"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Ανενεργή"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Ενεργή"</string> diff --git a/packages/SettingsLib/res/values-en-rAU/strings.xml b/packages/SettingsLib/res/values-en-rAU/strings.xml index 4bb2d8a592f1..285c615f8224 100644 --- a/packages/SettingsLib/res/values-en-rAU/strings.xml +++ b/packages/SettingsLib/res/values-en-rAU/strings.xml @@ -545,19 +545,14 @@ <string name="user_need_lock_message" msgid="4311424336209509301">"Before you can create a restricted profile, you\'ll need to set up a screen lock to protect your apps and personal data."</string> <string name="user_set_lock_button" msgid="1427128184982594856">"Set lock"</string> <string name="user_switch_to_user" msgid="6975428297154968543">"Switch to <xliff:g id="USER_NAME">%s</xliff:g>"</string> - <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) --> - <skip /> - <!-- no translation found for user_nickname (262624187455825083) --> - <skip /> + <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Creating new user…"</string> + <string name="user_nickname" msgid="262624187455825083">"Nickname"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Add guest"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Remove guest"</string> <string name="guest_nickname" msgid="6332276931583337261">"Guest"</string> - <!-- no translation found for user_image_take_photo (467512954561638530) --> - <skip /> - <!-- no translation found for user_image_choose_photo (1363820919146782908) --> - <skip /> - <!-- no translation found for user_image_photo_selector (433658323306627093) --> - <skip /> + <string name="user_image_take_photo" msgid="467512954561638530">"Take a photo"</string> + <string name="user_image_choose_photo" msgid="1363820919146782908">"Choose an image"</string> + <string name="user_image_photo_selector" msgid="433658323306627093">"Select photo"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Device default"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Disabled"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Enabled"</string> diff --git a/packages/SettingsLib/res/values-en-rCA/strings.xml b/packages/SettingsLib/res/values-en-rCA/strings.xml index 05c12d680c89..bbd5c7600818 100644 --- a/packages/SettingsLib/res/values-en-rCA/strings.xml +++ b/packages/SettingsLib/res/values-en-rCA/strings.xml @@ -545,19 +545,14 @@ <string name="user_need_lock_message" msgid="4311424336209509301">"Before you can create a restricted profile, you\'ll need to set up a screen lock to protect your apps and personal data."</string> <string name="user_set_lock_button" msgid="1427128184982594856">"Set lock"</string> <string name="user_switch_to_user" msgid="6975428297154968543">"Switch to <xliff:g id="USER_NAME">%s</xliff:g>"</string> - <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) --> - <skip /> - <!-- no translation found for user_nickname (262624187455825083) --> - <skip /> + <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Creating new user…"</string> + <string name="user_nickname" msgid="262624187455825083">"Nickname"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Add guest"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Remove guest"</string> <string name="guest_nickname" msgid="6332276931583337261">"Guest"</string> - <!-- no translation found for user_image_take_photo (467512954561638530) --> - <skip /> - <!-- no translation found for user_image_choose_photo (1363820919146782908) --> - <skip /> - <!-- no translation found for user_image_photo_selector (433658323306627093) --> - <skip /> + <string name="user_image_take_photo" msgid="467512954561638530">"Take a photo"</string> + <string name="user_image_choose_photo" msgid="1363820919146782908">"Choose an image"</string> + <string name="user_image_photo_selector" msgid="433658323306627093">"Select photo"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Device default"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Disabled"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Enabled"</string> diff --git a/packages/SettingsLib/res/values-en-rGB/strings.xml b/packages/SettingsLib/res/values-en-rGB/strings.xml index 4bb2d8a592f1..285c615f8224 100644 --- a/packages/SettingsLib/res/values-en-rGB/strings.xml +++ b/packages/SettingsLib/res/values-en-rGB/strings.xml @@ -545,19 +545,14 @@ <string name="user_need_lock_message" msgid="4311424336209509301">"Before you can create a restricted profile, you\'ll need to set up a screen lock to protect your apps and personal data."</string> <string name="user_set_lock_button" msgid="1427128184982594856">"Set lock"</string> <string name="user_switch_to_user" msgid="6975428297154968543">"Switch to <xliff:g id="USER_NAME">%s</xliff:g>"</string> - <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) --> - <skip /> - <!-- no translation found for user_nickname (262624187455825083) --> - <skip /> + <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Creating new user…"</string> + <string name="user_nickname" msgid="262624187455825083">"Nickname"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Add guest"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Remove guest"</string> <string name="guest_nickname" msgid="6332276931583337261">"Guest"</string> - <!-- no translation found for user_image_take_photo (467512954561638530) --> - <skip /> - <!-- no translation found for user_image_choose_photo (1363820919146782908) --> - <skip /> - <!-- no translation found for user_image_photo_selector (433658323306627093) --> - <skip /> + <string name="user_image_take_photo" msgid="467512954561638530">"Take a photo"</string> + <string name="user_image_choose_photo" msgid="1363820919146782908">"Choose an image"</string> + <string name="user_image_photo_selector" msgid="433658323306627093">"Select photo"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Device default"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Disabled"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Enabled"</string> diff --git a/packages/SettingsLib/res/values-en-rIN/strings.xml b/packages/SettingsLib/res/values-en-rIN/strings.xml index 4bb2d8a592f1..285c615f8224 100644 --- a/packages/SettingsLib/res/values-en-rIN/strings.xml +++ b/packages/SettingsLib/res/values-en-rIN/strings.xml @@ -545,19 +545,14 @@ <string name="user_need_lock_message" msgid="4311424336209509301">"Before you can create a restricted profile, you\'ll need to set up a screen lock to protect your apps and personal data."</string> <string name="user_set_lock_button" msgid="1427128184982594856">"Set lock"</string> <string name="user_switch_to_user" msgid="6975428297154968543">"Switch to <xliff:g id="USER_NAME">%s</xliff:g>"</string> - <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) --> - <skip /> - <!-- no translation found for user_nickname (262624187455825083) --> - <skip /> + <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Creating new user…"</string> + <string name="user_nickname" msgid="262624187455825083">"Nickname"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Add guest"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Remove guest"</string> <string name="guest_nickname" msgid="6332276931583337261">"Guest"</string> - <!-- no translation found for user_image_take_photo (467512954561638530) --> - <skip /> - <!-- no translation found for user_image_choose_photo (1363820919146782908) --> - <skip /> - <!-- no translation found for user_image_photo_selector (433658323306627093) --> - <skip /> + <string name="user_image_take_photo" msgid="467512954561638530">"Take a photo"</string> + <string name="user_image_choose_photo" msgid="1363820919146782908">"Choose an image"</string> + <string name="user_image_photo_selector" msgid="433658323306627093">"Select photo"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Device default"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Disabled"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Enabled"</string> diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml index 1dc5f5b332fc..ab421f6f4154 100644 --- a/packages/SettingsLib/res/values-es-rUS/strings.xml +++ b/packages/SettingsLib/res/values-es-rUS/strings.xml @@ -545,19 +545,14 @@ <string name="user_need_lock_message" msgid="4311424336209509301">"Para poder crear un perfil restringido, debes configurar un bloqueo de pantalla que proteja tus aplicaciones y datos personales."</string> <string name="user_set_lock_button" msgid="1427128184982594856">"Configurar bloqueo"</string> <string name="user_switch_to_user" msgid="6975428297154968543">"Cambiar a <xliff:g id="USER_NAME">%s</xliff:g>"</string> - <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) --> - <skip /> - <!-- no translation found for user_nickname (262624187455825083) --> - <skip /> + <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Creando usuario nuevo…"</string> + <string name="user_nickname" msgid="262624187455825083">"Sobrenombre"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Agregar invitado"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Quitar invitado"</string> <string name="guest_nickname" msgid="6332276931583337261">"Invitado"</string> - <!-- no translation found for user_image_take_photo (467512954561638530) --> - <skip /> - <!-- no translation found for user_image_choose_photo (1363820919146782908) --> - <skip /> - <!-- no translation found for user_image_photo_selector (433658323306627093) --> - <skip /> + <string name="user_image_take_photo" msgid="467512954561638530">"Tomar una foto"</string> + <string name="user_image_choose_photo" msgid="1363820919146782908">"Elegir una imagen"</string> + <string name="user_image_photo_selector" msgid="433658323306627093">"Seleccionar foto"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Predeterminado del dispositivo"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Inhabilitado"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Habilitado"</string> diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml index 977b4694d6c6..f6157b42312f 100644 --- a/packages/SettingsLib/res/values-es/strings.xml +++ b/packages/SettingsLib/res/values-es/strings.xml @@ -545,19 +545,14 @@ <string name="user_need_lock_message" msgid="4311424336209509301">"Para poder crear un perfil restringido, debes configurar una pantalla de bloqueo que proteja tus aplicaciones y datos personales."</string> <string name="user_set_lock_button" msgid="1427128184982594856">"Establecer bloqueo"</string> <string name="user_switch_to_user" msgid="6975428297154968543">"Cambiar a <xliff:g id="USER_NAME">%s</xliff:g>"</string> - <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) --> - <skip /> - <!-- no translation found for user_nickname (262624187455825083) --> - <skip /> + <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Creando usuario…"</string> + <string name="user_nickname" msgid="262624187455825083">"Apodo"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Añadir invitado"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Quitar invitado"</string> <string name="guest_nickname" msgid="6332276931583337261">"Invitado"</string> - <!-- no translation found for user_image_take_photo (467512954561638530) --> - <skip /> - <!-- no translation found for user_image_choose_photo (1363820919146782908) --> - <skip /> - <!-- no translation found for user_image_photo_selector (433658323306627093) --> - <skip /> + <string name="user_image_take_photo" msgid="467512954561638530">"Hacer foto"</string> + <string name="user_image_choose_photo" msgid="1363820919146782908">"Seleccionar una imagen"</string> + <string name="user_image_photo_selector" msgid="433658323306627093">"Seleccionar foto"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Predeterminado por el dispositivo"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Inhabilitado"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Habilitado"</string> diff --git a/packages/SettingsLib/res/values-et/strings.xml b/packages/SettingsLib/res/values-et/strings.xml index 3c2593aa1ed7..72766c067525 100644 --- a/packages/SettingsLib/res/values-et/strings.xml +++ b/packages/SettingsLib/res/values-et/strings.xml @@ -545,19 +545,14 @@ <string name="user_need_lock_message" msgid="4311424336209509301">"Enne piiratud profiili loomist peate seadistama lukustusekraani, et oma rakendusi ja isiklikke andmeid kaitsta."</string> <string name="user_set_lock_button" msgid="1427128184982594856">"Määra lukk"</string> <string name="user_switch_to_user" msgid="6975428297154968543">"Lülita kasutajale <xliff:g id="USER_NAME">%s</xliff:g>"</string> - <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) --> - <skip /> - <!-- no translation found for user_nickname (262624187455825083) --> - <skip /> + <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Uue kasutaja loomine …"</string> + <string name="user_nickname" msgid="262624187455825083">"Hüüdnimi"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Lisa külaline"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Eemalda külaline"</string> <string name="guest_nickname" msgid="6332276931583337261">"Külaline"</string> - <!-- no translation found for user_image_take_photo (467512954561638530) --> - <skip /> - <!-- no translation found for user_image_choose_photo (1363820919146782908) --> - <skip /> - <!-- no translation found for user_image_photo_selector (433658323306627093) --> - <skip /> + <string name="user_image_take_photo" msgid="467512954561638530">"Pildistage"</string> + <string name="user_image_choose_photo" msgid="1363820919146782908">"Valige pilt"</string> + <string name="user_image_photo_selector" msgid="433658323306627093">"Valige foto"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Seadme vaikeseade"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Keelatud"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Lubatud"</string> diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml index 33ce3c73959d..c7462cef36b7 100644 --- a/packages/SettingsLib/res/values-eu/strings.xml +++ b/packages/SettingsLib/res/values-eu/strings.xml @@ -545,19 +545,14 @@ <string name="user_need_lock_message" msgid="4311424336209509301">"Profil murriztua sortu aurretik, aplikazioak eta datu pertsonalak babesteko, pantaila blokeatzeko metodo bat konfiguratu beharko duzu."</string> <string name="user_set_lock_button" msgid="1427128184982594856">"Ezarri blokeoa"</string> <string name="user_switch_to_user" msgid="6975428297154968543">"Aldatu <xliff:g id="USER_NAME">%s</xliff:g> erabiltzailera"</string> - <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) --> - <skip /> - <!-- no translation found for user_nickname (262624187455825083) --> - <skip /> + <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Beste erabiltzaile bat sortzen…"</string> + <string name="user_nickname" msgid="262624187455825083">"Goitizena"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Gehitu gonbidatua"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Kendu gonbidatua"</string> <string name="guest_nickname" msgid="6332276931583337261">"Gonbidatua"</string> - <!-- no translation found for user_image_take_photo (467512954561638530) --> - <skip /> - <!-- no translation found for user_image_choose_photo (1363820919146782908) --> - <skip /> - <!-- no translation found for user_image_photo_selector (433658323306627093) --> - <skip /> + <string name="user_image_take_photo" msgid="467512954561638530">"Atera argazki bat"</string> + <string name="user_image_choose_photo" msgid="1363820919146782908">"Aukeratu irudi bat"</string> + <string name="user_image_photo_selector" msgid="433658323306627093">"Hautatu argazki bat"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Gailuaren balio lehenetsia"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Desgaituta"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Gaituta"</string> diff --git a/packages/SettingsLib/res/values-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml index 045378886eaa..0b21a2d6c8c4 100644 --- a/packages/SettingsLib/res/values-fa/strings.xml +++ b/packages/SettingsLib/res/values-fa/strings.xml @@ -28,7 +28,7 @@ <string name="wifi_disabled_network_failure" msgid="2660396183242399585">"پیکربندی IP انجام نشد"</string> <string name="wifi_disabled_by_recommendation_provider" msgid="1302938248432705534">"اتصال ناموفق به دلیل شبکه با کیفیت پایین"</string> <string name="wifi_disabled_wifi_failure" msgid="8819554899148331100">"اتصال Wi-Fi برقرار نشد"</string> - <string name="wifi_disabled_password_failure" msgid="6892387079613226738">"مشکل احراز هویت"</string> + <string name="wifi_disabled_password_failure" msgid="6892387079613226738">"مشکل اصالتسنجی"</string> <string name="wifi_cant_connect" msgid="5718417542623056783">"برقراری اتصال ممکن نیست"</string> <string name="wifi_cant_connect_to_ap" msgid="3099667989279700135">"برقراری اتصال به «<xliff:g id="AP_NAME">%1$s</xliff:g>» ممکن نیست"</string> <string name="wifi_check_password_try_again" msgid="8817789642851605628">"گذرواژه را بررسی و دوباره امتحان کنید"</string> @@ -545,19 +545,14 @@ <string name="user_need_lock_message" msgid="4311424336209509301">"قبل از ایجاد یک نمایه محدود، باید یک قفل صفحه را برای محافظت از برنامهها و دادههای شخصی خود تنظیم کنید."</string> <string name="user_set_lock_button" msgid="1427128184982594856">"تنظیم قفل"</string> <string name="user_switch_to_user" msgid="6975428297154968543">"رفتن به <xliff:g id="USER_NAME">%s</xliff:g>"</string> - <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) --> - <skip /> - <!-- no translation found for user_nickname (262624187455825083) --> - <skip /> + <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"درحال ایجاد کاربر جدید…"</string> + <string name="user_nickname" msgid="262624187455825083">"نام مستعار"</string> <string name="guest_new_guest" msgid="3482026122932643557">"افزودن مهمان"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"حذف مهمان"</string> <string name="guest_nickname" msgid="6332276931583337261">"مهمان"</string> - <!-- no translation found for user_image_take_photo (467512954561638530) --> - <skip /> - <!-- no translation found for user_image_choose_photo (1363820919146782908) --> - <skip /> - <!-- no translation found for user_image_photo_selector (433658323306627093) --> - <skip /> + <string name="user_image_take_photo" msgid="467512954561638530">"عکس گرفتن"</string> + <string name="user_image_choose_photo" msgid="1363820919146782908">"انتخاب تصویر"</string> + <string name="user_image_photo_selector" msgid="433658323306627093">"انتخاب عکس"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"پیشفرض دستگاه"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"غیرفعال"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"فعال"</string> diff --git a/packages/SettingsLib/res/values-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml index 7f85572ba023..dd1c12a0dc27 100644 --- a/packages/SettingsLib/res/values-fi/strings.xml +++ b/packages/SettingsLib/res/values-fi/strings.xml @@ -545,19 +545,14 @@ <string name="user_need_lock_message" msgid="4311424336209509301">"Ennen kuin voit luoda rajoitetun profiilin, määritä näytön lukitus, joka suojelee sovelluksiasi ja henkilökohtaisia tietojasi."</string> <string name="user_set_lock_button" msgid="1427128184982594856">"Aseta lukitus"</string> <string name="user_switch_to_user" msgid="6975428297154968543">"Vaihda tähän käyttäjään: <xliff:g id="USER_NAME">%s</xliff:g>"</string> - <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) --> - <skip /> - <!-- no translation found for user_nickname (262624187455825083) --> - <skip /> + <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Luodaan uutta käyttäjää…"</string> + <string name="user_nickname" msgid="262624187455825083">"Lempinimi"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Lisää vieras"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Poista vieras"</string> <string name="guest_nickname" msgid="6332276931583337261">"Vieras"</string> - <!-- no translation found for user_image_take_photo (467512954561638530) --> - <skip /> - <!-- no translation found for user_image_choose_photo (1363820919146782908) --> - <skip /> - <!-- no translation found for user_image_photo_selector (433658323306627093) --> - <skip /> + <string name="user_image_take_photo" msgid="467512954561638530">"Ota valokuva"</string> + <string name="user_image_choose_photo" msgid="1363820919146782908">"Valitse kuva"</string> + <string name="user_image_photo_selector" msgid="433658323306627093">"Valitse valokuva"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Laitteen oletusasetus"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Ei käytössä"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Käytössä"</string> diff --git a/packages/SettingsLib/res/values-fr-rCA/strings.xml b/packages/SettingsLib/res/values-fr-rCA/strings.xml index 1745e0298871..f0364c614d56 100644 --- a/packages/SettingsLib/res/values-fr-rCA/strings.xml +++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml @@ -545,19 +545,14 @@ <string name="user_need_lock_message" msgid="4311424336209509301">"Avant de créer un profil limité, vous devez définir un écran de verrouillage pour protéger vos applications et vos données personnelles."</string> <string name="user_set_lock_button" msgid="1427128184982594856">"Définir verrouillage écran"</string> <string name="user_switch_to_user" msgid="6975428297154968543">"Passer à <xliff:g id="USER_NAME">%s</xliff:g>"</string> - <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) --> - <skip /> - <!-- no translation found for user_nickname (262624187455825083) --> - <skip /> + <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Créer un utilisateur…"</string> + <string name="user_nickname" msgid="262624187455825083">"Pseudo"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Ajouter un invité"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Supprimer l\'invité"</string> <string name="guest_nickname" msgid="6332276931583337261">"Invité"</string> - <!-- no translation found for user_image_take_photo (467512954561638530) --> - <skip /> - <!-- no translation found for user_image_choose_photo (1363820919146782908) --> - <skip /> - <!-- no translation found for user_image_photo_selector (433658323306627093) --> - <skip /> + <string name="user_image_take_photo" msgid="467512954561638530">"Prendre une photo"</string> + <string name="user_image_choose_photo" msgid="1363820919146782908">"Sélectionner une image"</string> + <string name="user_image_photo_selector" msgid="433658323306627093">"Sélectionnez une photo"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Valeur par défaut de l\'appareil"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Désactivé"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Activé"</string> diff --git a/packages/SettingsLib/res/values-fr/strings.xml b/packages/SettingsLib/res/values-fr/strings.xml index 8c753ae26d3a..17d13c1568df 100644 --- a/packages/SettingsLib/res/values-fr/strings.xml +++ b/packages/SettingsLib/res/values-fr/strings.xml @@ -545,19 +545,14 @@ <string name="user_need_lock_message" msgid="4311424336209509301">"Avant de créer un profil limité, vous devez définir un écran de verrouillage pour protéger vos applications et vos données personnelles."</string> <string name="user_set_lock_button" msgid="1427128184982594856">"Définir verrouillage écran"</string> <string name="user_switch_to_user" msgid="6975428297154968543">"Passer à <xliff:g id="USER_NAME">%s</xliff:g>"</string> - <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) --> - <skip /> - <!-- no translation found for user_nickname (262624187455825083) --> - <skip /> + <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Création d\'un nouvel utilisateur…"</string> + <string name="user_nickname" msgid="262624187455825083">"Pseudo"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Ajouter un invité"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Supprimer l\'invité"</string> <string name="guest_nickname" msgid="6332276931583337261">"Invité"</string> - <!-- no translation found for user_image_take_photo (467512954561638530) --> - <skip /> - <!-- no translation found for user_image_choose_photo (1363820919146782908) --> - <skip /> - <!-- no translation found for user_image_photo_selector (433658323306627093) --> - <skip /> + <string name="user_image_take_photo" msgid="467512954561638530">"Prendre une photo"</string> + <string name="user_image_choose_photo" msgid="1363820919146782908">"Choisir une image"</string> + <string name="user_image_photo_selector" msgid="433658323306627093">"Sélectionner une photo"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Paramètre par défaut"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Désactivé"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Activé"</string> diff --git a/packages/SettingsLib/res/values-gl/strings.xml b/packages/SettingsLib/res/values-gl/strings.xml index 414564b505e4..e0b21c38ff67 100644 --- a/packages/SettingsLib/res/values-gl/strings.xml +++ b/packages/SettingsLib/res/values-gl/strings.xml @@ -545,19 +545,14 @@ <string name="user_need_lock_message" msgid="4311424336209509301">"Para poder crear un perfil restrinxido, precisarás configurar un bloqueo da pantalla para protexer as túas aplicacións e datos persoais."</string> <string name="user_set_lock_button" msgid="1427128184982594856">"Establecer bloqueo"</string> <string name="user_switch_to_user" msgid="6975428297154968543">"Cambiar a <xliff:g id="USER_NAME">%s</xliff:g>"</string> - <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) --> - <skip /> - <!-- no translation found for user_nickname (262624187455825083) --> - <skip /> + <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Creando usuario novo…"</string> + <string name="user_nickname" msgid="262624187455825083">"Alcume"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Engadir convidado"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Quitar convidado"</string> <string name="guest_nickname" msgid="6332276931583337261">"Convidado"</string> - <!-- no translation found for user_image_take_photo (467512954561638530) --> - <skip /> - <!-- no translation found for user_image_choose_photo (1363820919146782908) --> - <skip /> - <!-- no translation found for user_image_photo_selector (433658323306627093) --> - <skip /> + <string name="user_image_take_photo" msgid="467512954561638530">"Tirar foto"</string> + <string name="user_image_choose_photo" msgid="1363820919146782908">"Escoller imaxe"</string> + <string name="user_image_photo_selector" msgid="433658323306627093">"Seleccionar foto"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Funcionamento predeterminado"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Desactivado"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Activado"</string> diff --git a/packages/SettingsLib/res/values-gu/strings.xml b/packages/SettingsLib/res/values-gu/strings.xml index 634870ce77a4..08a2b99458cd 100644 --- a/packages/SettingsLib/res/values-gu/strings.xml +++ b/packages/SettingsLib/res/values-gu/strings.xml @@ -545,19 +545,14 @@ <string name="user_need_lock_message" msgid="4311424336209509301">"તમે પ્રતિબંધિત પ્રોફાઇલ બનાવી શકો તે પહેલાં, તમારે તમારી ઍપ્લિકેશનો અને વ્યક્તિગત ડેટાની સુરક્ષા માટે એક લૉક સ્ક્રીન સેટ કરવાની જરૂર પડશે."</string> <string name="user_set_lock_button" msgid="1427128184982594856">"લૉક સેટ કરો"</string> <string name="user_switch_to_user" msgid="6975428297154968543">"<xliff:g id="USER_NAME">%s</xliff:g> પર સ્વિચ કરો"</string> - <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) --> - <skip /> - <!-- no translation found for user_nickname (262624187455825083) --> - <skip /> + <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"નવા વપરાશકર્તા બનાવી રહ્યાં છીએ…"</string> + <string name="user_nickname" msgid="262624187455825083">"ઉપનામ"</string> <string name="guest_new_guest" msgid="3482026122932643557">"અતિથિ ઉમેરો"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"અતિથિને કાઢી નાખો"</string> <string name="guest_nickname" msgid="6332276931583337261">"અતિથિ"</string> - <!-- no translation found for user_image_take_photo (467512954561638530) --> - <skip /> - <!-- no translation found for user_image_choose_photo (1363820919146782908) --> - <skip /> - <!-- no translation found for user_image_photo_selector (433658323306627093) --> - <skip /> + <string name="user_image_take_photo" msgid="467512954561638530">"ફોટો લો"</string> + <string name="user_image_choose_photo" msgid="1363820919146782908">"છબી પસંદ કરો"</string> + <string name="user_image_photo_selector" msgid="433658323306627093">"ફોટો પસંદ કરો"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"ડિવાઇસ ડિફૉલ્ટ"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"બંધ છે"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"ચાલુ છે"</string> diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml index bd052c03ccb1..8486e3444db6 100644 --- a/packages/SettingsLib/res/values-hi/strings.xml +++ b/packages/SettingsLib/res/values-hi/strings.xml @@ -545,19 +545,14 @@ <string name="user_need_lock_message" msgid="4311424336209509301">"इससे पहले कि आप कोई प्रतिबंधित प्रोफ़ाइल बनाएं, आपको अपने ऐप्लिकेशन और व्यक्तिगत डेटा की सुरक्षा करने के लिए एक स्क्रीन लॉक सेट करना होगा."</string> <string name="user_set_lock_button" msgid="1427128184982594856">"लॉक सेट करें"</string> <string name="user_switch_to_user" msgid="6975428297154968543">"<xliff:g id="USER_NAME">%s</xliff:g> पर जाएं"</string> - <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) --> - <skip /> - <!-- no translation found for user_nickname (262624187455825083) --> - <skip /> + <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"नया उपयोगकर्ता बनाया जा रहा है…"</string> + <string name="user_nickname" msgid="262624187455825083">"प्रचलित नाम"</string> <string name="guest_new_guest" msgid="3482026122932643557">"मेहमान जोड़ें"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"मेहमान हटाएं"</string> <string name="guest_nickname" msgid="6332276931583337261">"मेहमान"</string> - <!-- no translation found for user_image_take_photo (467512954561638530) --> - <skip /> - <!-- no translation found for user_image_choose_photo (1363820919146782908) --> - <skip /> - <!-- no translation found for user_image_photo_selector (433658323306627093) --> - <skip /> + <string name="user_image_take_photo" msgid="467512954561638530">"फ़ोटो खींचें"</string> + <string name="user_image_choose_photo" msgid="1363820919146782908">"कोई इमेज चुनें"</string> + <string name="user_image_photo_selector" msgid="433658323306627093">"फ़ोटो चुनें"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"डिवाइस की डिफ़ॉल्ट सेटिंग"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"बंद है"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"चालू है"</string> diff --git a/packages/SettingsLib/res/values-hr/strings.xml b/packages/SettingsLib/res/values-hr/strings.xml index 3cff48068dda..a4ef3230fb5e 100644 --- a/packages/SettingsLib/res/values-hr/strings.xml +++ b/packages/SettingsLib/res/values-hr/strings.xml @@ -546,19 +546,14 @@ <string name="user_need_lock_message" msgid="4311424336209509301">"Prije izrade ograničenog profila trebate postaviti zaključavanje zaslona radi zaštite svojih aplikacija i osobnih podataka."</string> <string name="user_set_lock_button" msgid="1427128184982594856">"Postavi zaključavanje"</string> <string name="user_switch_to_user" msgid="6975428297154968543">"Prelazak na korisnika <xliff:g id="USER_NAME">%s</xliff:g>"</string> - <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) --> - <skip /> - <!-- no translation found for user_nickname (262624187455825083) --> - <skip /> + <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Izrada novog korisnika…"</string> + <string name="user_nickname" msgid="262624187455825083">"Nadimak"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Dodavanje gosta"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Uklanjanje gosta"</string> <string name="guest_nickname" msgid="6332276931583337261">"Gost"</string> - <!-- no translation found for user_image_take_photo (467512954561638530) --> - <skip /> - <!-- no translation found for user_image_choose_photo (1363820919146782908) --> - <skip /> - <!-- no translation found for user_image_photo_selector (433658323306627093) --> - <skip /> + <string name="user_image_take_photo" msgid="467512954561638530">"Fotografiraj"</string> + <string name="user_image_choose_photo" msgid="1363820919146782908">"Odaberi sliku"</string> + <string name="user_image_photo_selector" msgid="433658323306627093">"Odabir slike"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Zadana postavka uređaja"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Onemogućeno"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Omogućeno"</string> diff --git a/packages/SettingsLib/res/values-hu/strings.xml b/packages/SettingsLib/res/values-hu/strings.xml index 854265caf47b..480552b7b6b5 100644 --- a/packages/SettingsLib/res/values-hu/strings.xml +++ b/packages/SettingsLib/res/values-hu/strings.xml @@ -545,19 +545,14 @@ <string name="user_need_lock_message" msgid="4311424336209509301">"Mielőtt létrehozhatna egy korlátozott profilt, be kell állítania egy képernyőzárat, hogy megvédje alkalmazásait és személyes adatait."</string> <string name="user_set_lock_button" msgid="1427128184982594856">"Képernyőzár beállítása"</string> <string name="user_switch_to_user" msgid="6975428297154968543">"Váltás erre: <xliff:g id="USER_NAME">%s</xliff:g>"</string> - <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) --> - <skip /> - <!-- no translation found for user_nickname (262624187455825083) --> - <skip /> + <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Új felhasználó létrehozása…"</string> + <string name="user_nickname" msgid="262624187455825083">"Becenév"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Vendég hozzáadása"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Vendég munkamenet eltávolítása"</string> <string name="guest_nickname" msgid="6332276931583337261">"Vendég"</string> - <!-- no translation found for user_image_take_photo (467512954561638530) --> - <skip /> - <!-- no translation found for user_image_choose_photo (1363820919146782908) --> - <skip /> - <!-- no translation found for user_image_photo_selector (433658323306627093) --> - <skip /> + <string name="user_image_take_photo" msgid="467512954561638530">"Fotó készítése"</string> + <string name="user_image_choose_photo" msgid="1363820919146782908">"Kép kiválasztása"</string> + <string name="user_image_photo_selector" msgid="433658323306627093">"Fotó kiválasztása"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Alapértelmezett"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Letiltva"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Engedélyezve"</string> diff --git a/packages/SettingsLib/res/values-hy/strings.xml b/packages/SettingsLib/res/values-hy/strings.xml index 35b218edd1c2..9fbf998dd14f 100644 --- a/packages/SettingsLib/res/values-hy/strings.xml +++ b/packages/SettingsLib/res/values-hy/strings.xml @@ -545,19 +545,14 @@ <string name="user_need_lock_message" msgid="4311424336209509301">"Նախքան դուք կկարողանաք ստեղծել սահմանափակ պրոֆիլ, դուք պետք է կարգավորեք էկրանի կողպումը` ձեր ծրագրերը և անձնական տվյալները պաշտպանելու համար:"</string> <string name="user_set_lock_button" msgid="1427128184982594856">"Կարգավորել կողպումը"</string> <string name="user_switch_to_user" msgid="6975428297154968543">"Անցնել <xliff:g id="USER_NAME">%s</xliff:g> պրոֆիլին"</string> - <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) --> - <skip /> - <!-- no translation found for user_nickname (262624187455825083) --> - <skip /> + <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Ստեղծվում է օգտատիրոջ նոր պրոֆիլ…"</string> + <string name="user_nickname" msgid="262624187455825083">"Կեղծանուն"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Ավելացնել հյուր"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Հեռացնել հյուրին"</string> <string name="guest_nickname" msgid="6332276931583337261">"Հյուր"</string> - <!-- no translation found for user_image_take_photo (467512954561638530) --> - <skip /> - <!-- no translation found for user_image_choose_photo (1363820919146782908) --> - <skip /> - <!-- no translation found for user_image_photo_selector (433658323306627093) --> - <skip /> + <string name="user_image_take_photo" msgid="467512954561638530">"Լուսանկարել"</string> + <string name="user_image_choose_photo" msgid="1363820919146782908">"Ընտրել պատկեր"</string> + <string name="user_image_photo_selector" msgid="433658323306627093">"Ընտրեք լուսանկար"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Կանխադրված տարբերակ"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Անջատված է"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Միացված է"</string> diff --git a/packages/SettingsLib/res/values-in/strings.xml b/packages/SettingsLib/res/values-in/strings.xml index cf6455ffe0c6..b1391ecaae73 100644 --- a/packages/SettingsLib/res/values-in/strings.xml +++ b/packages/SettingsLib/res/values-in/strings.xml @@ -545,19 +545,14 @@ <string name="user_need_lock_message" msgid="4311424336209509301">"Sebelum dapat membuat profil yang dibatasi, Anda perlu menyiapkan kunci layar untuk melindungi aplikasi dan data pribadi Anda."</string> <string name="user_set_lock_button" msgid="1427128184982594856">"Setel kunci"</string> <string name="user_switch_to_user" msgid="6975428297154968543">"Beralih ke <xliff:g id="USER_NAME">%s</xliff:g>"</string> - <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) --> - <skip /> - <!-- no translation found for user_nickname (262624187455825083) --> - <skip /> + <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Membuat pengguna baru …"</string> + <string name="user_nickname" msgid="262624187455825083">"Nama panggilan"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Tambahkan tamu"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Hapus tamu"</string> <string name="guest_nickname" msgid="6332276931583337261">"Tamu"</string> - <!-- no translation found for user_image_take_photo (467512954561638530) --> - <skip /> - <!-- no translation found for user_image_choose_photo (1363820919146782908) --> - <skip /> - <!-- no translation found for user_image_photo_selector (433658323306627093) --> - <skip /> + <string name="user_image_take_photo" msgid="467512954561638530">"Ambil foto"</string> + <string name="user_image_choose_photo" msgid="1363820919146782908">"Pilih gambar"</string> + <string name="user_image_photo_selector" msgid="433658323306627093">"Pilih foto"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Default perangkat"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Nonaktif"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Aktif"</string> diff --git a/packages/SettingsLib/res/values-is/strings.xml b/packages/SettingsLib/res/values-is/strings.xml index b4be963b44b2..1636c5542a8d 100644 --- a/packages/SettingsLib/res/values-is/strings.xml +++ b/packages/SettingsLib/res/values-is/strings.xml @@ -545,19 +545,14 @@ <string name="user_need_lock_message" msgid="4311424336209509301">"Áður en þú getur búið til takmarkað snið þarftu að setja upp skjálás til að vernda forritin þín og persónuleg gögn."</string> <string name="user_set_lock_button" msgid="1427128184982594856">"Velja lás"</string> <string name="user_switch_to_user" msgid="6975428297154968543">"Skipta yfir í <xliff:g id="USER_NAME">%s</xliff:g>"</string> - <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) --> - <skip /> - <!-- no translation found for user_nickname (262624187455825083) --> - <skip /> + <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Stofnar nýjan notanda…"</string> + <string name="user_nickname" msgid="262624187455825083">"Gælunafn"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Bæta gesti við"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Fjarlægja gest"</string> <string name="guest_nickname" msgid="6332276931583337261">"Gestur"</string> - <!-- no translation found for user_image_take_photo (467512954561638530) --> - <skip /> - <!-- no translation found for user_image_choose_photo (1363820919146782908) --> - <skip /> - <!-- no translation found for user_image_photo_selector (433658323306627093) --> - <skip /> + <string name="user_image_take_photo" msgid="467512954561638530">"Taka mynd"</string> + <string name="user_image_choose_photo" msgid="1363820919146782908">"Velja mynd"</string> + <string name="user_image_photo_selector" msgid="433658323306627093">"Velja mynd"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Sjálfgefin stilling tækis"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Slökkt"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Virkt"</string> diff --git a/packages/SettingsLib/res/values-it/strings.xml b/packages/SettingsLib/res/values-it/strings.xml index 69b1b5eed9c9..b358d651ac19 100644 --- a/packages/SettingsLib/res/values-it/strings.xml +++ b/packages/SettingsLib/res/values-it/strings.xml @@ -545,19 +545,14 @@ <string name="user_need_lock_message" msgid="4311424336209509301">"Prima di poter creare un profilo con limitazioni, devi impostare un blocco schermo per proteggere le tue app e i tuoi dati personali."</string> <string name="user_set_lock_button" msgid="1427128184982594856">"Imposta blocco"</string> <string name="user_switch_to_user" msgid="6975428297154968543">"Passa a <xliff:g id="USER_NAME">%s</xliff:g>"</string> - <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) --> - <skip /> - <!-- no translation found for user_nickname (262624187455825083) --> - <skip /> + <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Creazione nuovo utente…"</string> + <string name="user_nickname" msgid="262624187455825083">"Nickname"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Aggiungi ospite"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Rimuovi ospite"</string> <string name="guest_nickname" msgid="6332276931583337261">"Ospite"</string> - <!-- no translation found for user_image_take_photo (467512954561638530) --> - <skip /> - <!-- no translation found for user_image_choose_photo (1363820919146782908) --> - <skip /> - <!-- no translation found for user_image_photo_selector (433658323306627093) --> - <skip /> + <string name="user_image_take_photo" msgid="467512954561638530">"Scatta una foto"</string> + <string name="user_image_choose_photo" msgid="1363820919146782908">"Scegli un\'immagine"</string> + <string name="user_image_photo_selector" msgid="433658323306627093">"Seleziona la foto"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Parametro predefinito"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Non attivo"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Attivo"</string> diff --git a/packages/SettingsLib/res/values-iw/strings.xml b/packages/SettingsLib/res/values-iw/strings.xml index 158872df8365..0f2eb4bd5fb1 100644 --- a/packages/SettingsLib/res/values-iw/strings.xml +++ b/packages/SettingsLib/res/values-iw/strings.xml @@ -547,19 +547,14 @@ <string name="user_need_lock_message" msgid="4311424336209509301">"לפני שתוכל ליצור פרופיל מוגבל, תצטרך להגדיר נעילת מסך כדי להגן על האפליקציות ועל הנתונים האישיים שלך."</string> <string name="user_set_lock_button" msgid="1427128184982594856">"הגדרת נעילה"</string> <string name="user_switch_to_user" msgid="6975428297154968543">"מעבר אל <xliff:g id="USER_NAME">%s</xliff:g>"</string> - <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) --> - <skip /> - <!-- no translation found for user_nickname (262624187455825083) --> - <skip /> + <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"בתהליך יצירה של משתמש חדש…"</string> + <string name="user_nickname" msgid="262624187455825083">"כינוי"</string> <string name="guest_new_guest" msgid="3482026122932643557">"הוספת אורח"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"הסרת אורח"</string> <string name="guest_nickname" msgid="6332276931583337261">"אורח"</string> - <!-- no translation found for user_image_take_photo (467512954561638530) --> - <skip /> - <!-- no translation found for user_image_choose_photo (1363820919146782908) --> - <skip /> - <!-- no translation found for user_image_photo_selector (433658323306627093) --> - <skip /> + <string name="user_image_take_photo" msgid="467512954561638530">"צילום תמונה"</string> + <string name="user_image_choose_photo" msgid="1363820919146782908">"לבחירת תמונה"</string> + <string name="user_image_photo_selector" msgid="433658323306627093">"בחירת תמונה"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"ברירת המחדל של המכשיר"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"מושבת"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"מופעל"</string> diff --git a/packages/SettingsLib/res/values-ja/strings.xml b/packages/SettingsLib/res/values-ja/strings.xml index 13fd53fc1198..dba1c95aade0 100644 --- a/packages/SettingsLib/res/values-ja/strings.xml +++ b/packages/SettingsLib/res/values-ja/strings.xml @@ -545,19 +545,14 @@ <string name="user_need_lock_message" msgid="4311424336209509301">"制限付きプロファイルを作成する場合は、アプリや個人データを保護するように画面ロックを設定しておく必要があります。"</string> <string name="user_set_lock_button" msgid="1427128184982594856">"ロックを設定"</string> <string name="user_switch_to_user" msgid="6975428297154968543">"<xliff:g id="USER_NAME">%s</xliff:g> に切り替え"</string> - <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) --> - <skip /> - <!-- no translation found for user_nickname (262624187455825083) --> - <skip /> + <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"新しいユーザーを作成しています…"</string> + <string name="user_nickname" msgid="262624187455825083">"ニックネーム"</string> <string name="guest_new_guest" msgid="3482026122932643557">"ゲストを追加"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"ゲストを削除"</string> <string name="guest_nickname" msgid="6332276931583337261">"ゲスト"</string> - <!-- no translation found for user_image_take_photo (467512954561638530) --> - <skip /> - <!-- no translation found for user_image_choose_photo (1363820919146782908) --> - <skip /> - <!-- no translation found for user_image_photo_selector (433658323306627093) --> - <skip /> + <string name="user_image_take_photo" msgid="467512954561638530">"写真を撮る"</string> + <string name="user_image_choose_photo" msgid="1363820919146782908">"画像を選択"</string> + <string name="user_image_photo_selector" msgid="433658323306627093">"写真を選択"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"デバイスのデフォルト"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"無効"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"有効"</string> diff --git a/packages/SettingsLib/res/values-kk/strings.xml b/packages/SettingsLib/res/values-kk/strings.xml index f411d00c595d..ffb5c9aa27a6 100644 --- a/packages/SettingsLib/res/values-kk/strings.xml +++ b/packages/SettingsLib/res/values-kk/strings.xml @@ -545,19 +545,14 @@ <string name="user_need_lock_message" msgid="4311424336209509301">"Шектелген профайл жасақтауға дейін қолданбалар мен жеке деректерді қорғау үшін экран бекітпесін тағайындау қажет."</string> <string name="user_set_lock_button" msgid="1427128184982594856">"Бекітпе тағайындау"</string> <string name="user_switch_to_user" msgid="6975428297154968543">"<xliff:g id="USER_NAME">%s</xliff:g> пайдаланушысына ауысу"</string> - <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) --> - <skip /> - <!-- no translation found for user_nickname (262624187455825083) --> - <skip /> + <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Жаңа пайдаланушы профилі жасалуда…"</string> + <string name="user_nickname" msgid="262624187455825083">"Лақап ат"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Қонақты енгізу"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Қонақты өшіру"</string> <string name="guest_nickname" msgid="6332276931583337261">"Қонақ"</string> - <!-- no translation found for user_image_take_photo (467512954561638530) --> - <skip /> - <!-- no translation found for user_image_choose_photo (1363820919146782908) --> - <skip /> - <!-- no translation found for user_image_photo_selector (433658323306627093) --> - <skip /> + <string name="user_image_take_photo" msgid="467512954561638530">"Фотосуретке түсіру"</string> + <string name="user_image_choose_photo" msgid="1363820919146782908">"Сурет таңдау"</string> + <string name="user_image_photo_selector" msgid="433658323306627093">"Фотосурет таңдау"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Құрылғыны әдепкісінше реттеу"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Өшірулі"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Қосулы"</string> diff --git a/packages/SettingsLib/res/values-km/strings.xml b/packages/SettingsLib/res/values-km/strings.xml index 9b7cc4c1ab47..6fbd2d124538 100644 --- a/packages/SettingsLib/res/values-km/strings.xml +++ b/packages/SettingsLib/res/values-km/strings.xml @@ -545,19 +545,14 @@ <string name="user_need_lock_message" msgid="4311424336209509301">"មុនពេលអ្នកអាចបង្កើតប្រវត្តិរូបបានដាក់កម្រិត អ្នកត្រូវរៀបចំការចាក់សោអេក្រង់ ដើម្បីការពារកម្មវិធី និងទិន្នន័យផ្ទាល់ខ្លួនរបស់អ្នក។"</string> <string name="user_set_lock_button" msgid="1427128184982594856">"កំណត់ការចាក់សោ"</string> <string name="user_switch_to_user" msgid="6975428297154968543">"ប្ដូរទៅ <xliff:g id="USER_NAME">%s</xliff:g>"</string> - <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) --> - <skip /> - <!-- no translation found for user_nickname (262624187455825083) --> - <skip /> + <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"កំពុងបង្កើតអ្នកប្រើប្រាស់ថ្មី…"</string> + <string name="user_nickname" msgid="262624187455825083">"ឈ្មោះហៅក្រៅ"</string> <string name="guest_new_guest" msgid="3482026122932643557">"បញ្ចូលភ្ញៀវ"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"លុបភ្ញៀវ"</string> <string name="guest_nickname" msgid="6332276931583337261">"ភ្ញៀវ"</string> - <!-- no translation found for user_image_take_photo (467512954561638530) --> - <skip /> - <!-- no translation found for user_image_choose_photo (1363820919146782908) --> - <skip /> - <!-- no translation found for user_image_photo_selector (433658323306627093) --> - <skip /> + <string name="user_image_take_photo" msgid="467512954561638530">"ថតរូប"</string> + <string name="user_image_choose_photo" msgid="1363820919146782908">"ជ្រើសរើសរូបភាព"</string> + <string name="user_image_photo_selector" msgid="433658323306627093">"ជ្រើសរើសរូបថត"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"លំនាំដើមរបស់ឧបករណ៍"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"បានបិទ"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"បានបើក"</string> diff --git a/packages/SettingsLib/res/values-kn/strings.xml b/packages/SettingsLib/res/values-kn/strings.xml index 1f74d9b60cc8..653d8ba062e9 100644 --- a/packages/SettingsLib/res/values-kn/strings.xml +++ b/packages/SettingsLib/res/values-kn/strings.xml @@ -545,19 +545,14 @@ <string name="user_need_lock_message" msgid="4311424336209509301">"ನೀವು ನಿರ್ಬಂಧಿತ ಪ್ರೊಫೈಲ್ ಅನ್ನು ರಚಿಸಬಹುದಾದರ ಮೊದಲು, ನಿಮ್ಮ ಅಪ್ಲಿಕೇಶನ್ಗಳು ಮತ್ತು ವೈಯಕ್ತಿಕ ಡೇಟಾವನ್ನು ರಕ್ಷಿಸಲು ನೀವು ಪರದೆಯ ಲಾಕ್ ಹೊಂದಿಸುವ ಅಗತ್ಯವಿದೆ."</string> <string name="user_set_lock_button" msgid="1427128184982594856">"ಲಾಕ್ ಹೊಂದಿಸಿ"</string> <string name="user_switch_to_user" msgid="6975428297154968543">"<xliff:g id="USER_NAME">%s</xliff:g> ಗೆ ಬದಲಿಸಿ"</string> - <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) --> - <skip /> - <!-- no translation found for user_nickname (262624187455825083) --> - <skip /> + <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"ಹೊಸ ಬಳಕೆದಾರರನ್ನು ರಚಿಸಲಾಗುತ್ತಿದೆ…"</string> + <string name="user_nickname" msgid="262624187455825083">"ಅಡ್ಡ ಹೆಸರು"</string> <string name="guest_new_guest" msgid="3482026122932643557">"ಅತಿಥಿಯನ್ನು ಸೇರಿಸಿ"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"ಅತಿಥಿಯನ್ನು ತೆಗೆದುಹಾಕಿ"</string> <string name="guest_nickname" msgid="6332276931583337261">"ಅತಿಥಿ"</string> - <!-- no translation found for user_image_take_photo (467512954561638530) --> - <skip /> - <!-- no translation found for user_image_choose_photo (1363820919146782908) --> - <skip /> - <!-- no translation found for user_image_photo_selector (433658323306627093) --> - <skip /> + <string name="user_image_take_photo" msgid="467512954561638530">"ಫೋಟೋ ತೆಗೆದುಕೊಳ್ಳಿ"</string> + <string name="user_image_choose_photo" msgid="1363820919146782908">"ಚಿತ್ರವನ್ನು ಆರಿಸಿ"</string> + <string name="user_image_photo_selector" msgid="433658323306627093">"ಫೋಟೋ ಆಯ್ಕೆಮಾಡಿ"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"ಸಾಧನದ ಡೀಫಾಲ್ಟ್"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"ಸಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ"</string> diff --git a/packages/SettingsLib/res/values-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml index d1308345a1f9..761f8133f569 100644 --- a/packages/SettingsLib/res/values-ko/strings.xml +++ b/packages/SettingsLib/res/values-ko/strings.xml @@ -545,19 +545,14 @@ <string name="user_need_lock_message" msgid="4311424336209509301">"제한된 프로필을 만들기 전에 화면 잠금을 설정하여 앱과 개인 데이터를 보호해야 합니다."</string> <string name="user_set_lock_button" msgid="1427128184982594856">"잠금 설정"</string> <string name="user_switch_to_user" msgid="6975428297154968543">"<xliff:g id="USER_NAME">%s</xliff:g>(으)로 전환"</string> - <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) --> - <skip /> - <!-- no translation found for user_nickname (262624187455825083) --> - <skip /> + <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"새로운 사용자를 만드는 중…"</string> + <string name="user_nickname" msgid="262624187455825083">"닉네임"</string> <string name="guest_new_guest" msgid="3482026122932643557">"게스트 추가"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"게스트 삭제"</string> <string name="guest_nickname" msgid="6332276931583337261">"게스트"</string> - <!-- no translation found for user_image_take_photo (467512954561638530) --> - <skip /> - <!-- no translation found for user_image_choose_photo (1363820919146782908) --> - <skip /> - <!-- no translation found for user_image_photo_selector (433658323306627093) --> - <skip /> + <string name="user_image_take_photo" msgid="467512954561638530">"사진 찍기"</string> + <string name="user_image_choose_photo" msgid="1363820919146782908">"이미지 선택"</string> + <string name="user_image_photo_selector" msgid="433658323306627093">"사진 선택"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"기기 기본값"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"사용 중지됨"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"사용 설정됨"</string> diff --git a/packages/SettingsLib/res/values-ky/strings.xml b/packages/SettingsLib/res/values-ky/strings.xml index 5672913174ee..c72b93f4498b 100644 --- a/packages/SettingsLib/res/values-ky/strings.xml +++ b/packages/SettingsLib/res/values-ky/strings.xml @@ -545,19 +545,14 @@ <string name="user_need_lock_message" msgid="4311424336209509301">"Чектелген профайл түзөөрдөн мурун, сиз өзүңүздүн колдонмолоруңузду жана жеке маалыматтарыңызды коргош үчүн, бөгөттөө көшөгөсүн орнотушуңуз керек болот."</string> <string name="user_set_lock_button" msgid="1427128184982594856">"Бөгөт коюу"</string> <string name="user_switch_to_user" msgid="6975428297154968543">"<xliff:g id="USER_NAME">%s</xliff:g> аккаунтуна которулуу"</string> - <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) --> - <skip /> - <!-- no translation found for user_nickname (262624187455825083) --> - <skip /> + <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Жаңы колдонуучу түзүлүүдө…"</string> + <string name="user_nickname" msgid="262624187455825083">"Ылакап аты"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Конок кошуу"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Конокту өчүрүү"</string> <string name="guest_nickname" msgid="6332276931583337261">"Конок"</string> - <!-- no translation found for user_image_take_photo (467512954561638530) --> - <skip /> - <!-- no translation found for user_image_choose_photo (1363820919146782908) --> - <skip /> - <!-- no translation found for user_image_photo_selector (433658323306627093) --> - <skip /> + <string name="user_image_take_photo" msgid="467512954561638530">"Сүрөткө тартуу"</string> + <string name="user_image_choose_photo" msgid="1363820919146782908">"Сүрөт тандаңыз"</string> + <string name="user_image_photo_selector" msgid="433658323306627093">"Сүрөт тандаңыз"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Түзмөктүн демейки параметри"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Өчүк"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Күйүк"</string> diff --git a/packages/SettingsLib/res/values-lo/strings.xml b/packages/SettingsLib/res/values-lo/strings.xml index b3b3c8107d81..78833086d2ca 100644 --- a/packages/SettingsLib/res/values-lo/strings.xml +++ b/packages/SettingsLib/res/values-lo/strings.xml @@ -545,19 +545,14 @@ <string name="user_need_lock_message" msgid="4311424336209509301">"ກ່ອນທ່ານຈະສ້າງໂປຣໄຟລ໌ທີ່ຖືກຈຳກັດນັ້ນ, ທ່ານຈະຕ້ອງຕັ້ງຄ່າການລັອກໜ້າຈໍ ເພື່ອປ້ອງກັນແອັບຯ ແລະຂໍ້ມູນສ່ວນໂຕຂອງທ່ານກ່ອນ."</string> <string name="user_set_lock_button" msgid="1427128184982594856">"ຕັ້ງການລັອກ"</string> <string name="user_switch_to_user" msgid="6975428297154968543">"ສະຫຼັບໄປ <xliff:g id="USER_NAME">%s</xliff:g>"</string> - <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) --> - <skip /> - <!-- no translation found for user_nickname (262624187455825083) --> - <skip /> + <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"ກຳລັງສ້າງຜູ້ໃຊ້ໃໝ່…"</string> + <string name="user_nickname" msgid="262624187455825083">"ຊື່ຫຼິ້ນ"</string> <string name="guest_new_guest" msgid="3482026122932643557">"ເພີ່ມແຂກ"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"ລຶບແຂກອອກ"</string> <string name="guest_nickname" msgid="6332276931583337261">"ແຂກ"</string> - <!-- no translation found for user_image_take_photo (467512954561638530) --> - <skip /> - <!-- no translation found for user_image_choose_photo (1363820919146782908) --> - <skip /> - <!-- no translation found for user_image_photo_selector (433658323306627093) --> - <skip /> + <string name="user_image_take_photo" msgid="467512954561638530">"ຖ່າຍຮູບ"</string> + <string name="user_image_choose_photo" msgid="1363820919146782908">"ເລືອກຮູບ"</string> + <string name="user_image_photo_selector" msgid="433658323306627093">"ເລືອກຮູບ"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"ຄ່າເລີ່ມຕົ້ນອຸປະກອນ"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"ປິດການນຳໃຊ້ແລ້ວ"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"ເປີດການນຳໃຊ້ແລ້ວ"</string> diff --git a/packages/SettingsLib/res/values-lt/strings.xml b/packages/SettingsLib/res/values-lt/strings.xml index 2e5385ecc770..e3aa3687d5f9 100644 --- a/packages/SettingsLib/res/values-lt/strings.xml +++ b/packages/SettingsLib/res/values-lt/strings.xml @@ -547,19 +547,14 @@ <string name="user_need_lock_message" msgid="4311424336209509301">"Prieš kuriant apribotą profilį reikės nustatyti ekrano užraktą, kad apsaugotumėte programas ir asmeninius duomenis."</string> <string name="user_set_lock_button" msgid="1427128184982594856">"Nustatyti užraktą"</string> <string name="user_switch_to_user" msgid="6975428297154968543">"Perjungti į <xliff:g id="USER_NAME">%s</xliff:g>"</string> - <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) --> - <skip /> - <!-- no translation found for user_nickname (262624187455825083) --> - <skip /> + <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Kuriamas naujas naudotojas…"</string> + <string name="user_nickname" msgid="262624187455825083">"Slapyvardis"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Pridėti svečią"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Pašalinti svečią"</string> <string name="guest_nickname" msgid="6332276931583337261">"Svečias"</string> - <!-- no translation found for user_image_take_photo (467512954561638530) --> - <skip /> - <!-- no translation found for user_image_choose_photo (1363820919146782908) --> - <skip /> - <!-- no translation found for user_image_photo_selector (433658323306627093) --> - <skip /> + <string name="user_image_take_photo" msgid="467512954561638530">"Fotografuoti"</string> + <string name="user_image_choose_photo" msgid="1363820919146782908">"Pasirinkti vaizdą"</string> + <string name="user_image_photo_selector" msgid="433658323306627093">"Pasirinkti nuotrauką"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Numatyt. įrenginio nustatymas"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Išjungta"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Įgalinta"</string> diff --git a/packages/SettingsLib/res/values-lv/strings.xml b/packages/SettingsLib/res/values-lv/strings.xml index f4fe8e30a73b..e994974d813d 100644 --- a/packages/SettingsLib/res/values-lv/strings.xml +++ b/packages/SettingsLib/res/values-lv/strings.xml @@ -546,19 +546,14 @@ <string name="user_need_lock_message" msgid="4311424336209509301">"Lai varētu izveidot ierobežotu profilu, jums jāiestata ekrāna bloķēšana, kas aizsargās jūsu lietotni un personas datus."</string> <string name="user_set_lock_button" msgid="1427128184982594856">"Iestatīt bloķēšanu"</string> <string name="user_switch_to_user" msgid="6975428297154968543">"Pārslēgties uz: <xliff:g id="USER_NAME">%s</xliff:g>"</string> - <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) --> - <skip /> - <!-- no translation found for user_nickname (262624187455825083) --> - <skip /> + <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Notiek jauna lietotāja izveide…"</string> + <string name="user_nickname" msgid="262624187455825083">"Segvārds"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Pievienot viesi"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Noņemt viesi"</string> <string name="guest_nickname" msgid="6332276931583337261">"Viesis"</string> - <!-- no translation found for user_image_take_photo (467512954561638530) --> - <skip /> - <!-- no translation found for user_image_choose_photo (1363820919146782908) --> - <skip /> - <!-- no translation found for user_image_photo_selector (433658323306627093) --> - <skip /> + <string name="user_image_take_photo" msgid="467512954561638530">"Uzņemt fotoattēlu"</string> + <string name="user_image_choose_photo" msgid="1363820919146782908">"Izvēlēties attēlu"</string> + <string name="user_image_photo_selector" msgid="433658323306627093">"Atlasīt fotoattēlu"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Ierīces noklusējums"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Atspējots"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Iespējots"</string> diff --git a/packages/SettingsLib/res/values-mk/strings.xml b/packages/SettingsLib/res/values-mk/strings.xml index 1e933fbe5591..e711747f328f 100644 --- a/packages/SettingsLib/res/values-mk/strings.xml +++ b/packages/SettingsLib/res/values-mk/strings.xml @@ -545,19 +545,14 @@ <string name="user_need_lock_message" msgid="4311424336209509301">"Пред да може да создадете ограничен профил, треба да поставите заклучување на екранот за да ги заштити вашите апликации и лични податоци."</string> <string name="user_set_lock_button" msgid="1427128184982594856">"Постави заклучување"</string> <string name="user_switch_to_user" msgid="6975428297154968543">"Префрли на <xliff:g id="USER_NAME">%s</xliff:g>"</string> - <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) --> - <skip /> - <!-- no translation found for user_nickname (262624187455825083) --> - <skip /> + <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Се создава нов корисник…"</string> + <string name="user_nickname" msgid="262624187455825083">"Прекар"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Додај гостин"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Отстрани гостин"</string> <string name="guest_nickname" msgid="6332276931583337261">"Гостин"</string> - <!-- no translation found for user_image_take_photo (467512954561638530) --> - <skip /> - <!-- no translation found for user_image_choose_photo (1363820919146782908) --> - <skip /> - <!-- no translation found for user_image_photo_selector (433658323306627093) --> - <skip /> + <string name="user_image_take_photo" msgid="467512954561638530">"Фотографирајте"</string> + <string name="user_image_choose_photo" msgid="1363820919146782908">"Одберете слика"</string> + <string name="user_image_photo_selector" msgid="433658323306627093">"Изберете фотографија"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Стандардно за уредот"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Оневозможено"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Овозможено"</string> diff --git a/packages/SettingsLib/res/values-ml/strings.xml b/packages/SettingsLib/res/values-ml/strings.xml index 970f778119e3..cebd55213599 100644 --- a/packages/SettingsLib/res/values-ml/strings.xml +++ b/packages/SettingsLib/res/values-ml/strings.xml @@ -545,19 +545,14 @@ <string name="user_need_lock_message" msgid="4311424336209509301">"ഒരു നിയന്ത്രിത പ്രൊഫൈൽ സൃഷ്ടിക്കുന്നതിനുമുമ്പ്, നിങ്ങളുടെ അപ്ലിക്കേഷനുകളും വ്യക്തിഗത ഡാറ്റയും പരിരക്ഷിക്കുന്നതിന് ഒരു സ്ക്രീൻ ലോക്ക് സജ്ജീകരിക്കേണ്ടതുണ്ട്."</string> <string name="user_set_lock_button" msgid="1427128184982594856">"ലോക്ക് സജ്ജീകരിക്കുക"</string> <string name="user_switch_to_user" msgid="6975428297154968543">"<xliff:g id="USER_NAME">%s</xliff:g> എന്നതിലേക്ക് മാറുക"</string> - <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) --> - <skip /> - <!-- no translation found for user_nickname (262624187455825083) --> - <skip /> + <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"പുതിയ ഉപയോക്താവിനെ സൃഷ്ടിക്കുന്നു…"</string> + <string name="user_nickname" msgid="262624187455825083">"വിളിപ്പേര്"</string> <string name="guest_new_guest" msgid="3482026122932643557">"അതിഥിയെ ചേർക്കുക"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"അതിഥിയെ നീക്കം ചെയ്യുക"</string> <string name="guest_nickname" msgid="6332276931583337261">"അതിഥി"</string> - <!-- no translation found for user_image_take_photo (467512954561638530) --> - <skip /> - <!-- no translation found for user_image_choose_photo (1363820919146782908) --> - <skip /> - <!-- no translation found for user_image_photo_selector (433658323306627093) --> - <skip /> + <string name="user_image_take_photo" msgid="467512954561638530">"ഒരു ഫോട്ടോ എടുക്കുക"</string> + <string name="user_image_choose_photo" msgid="1363820919146782908">"ഒരു ചിത്രം തിരഞ്ഞെടുക്കുക"</string> + <string name="user_image_photo_selector" msgid="433658323306627093">"ഫോട്ടോ തിരഞ്ഞെടുക്കുക"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"ഉപകരണത്തിന്റെ ഡിഫോൾട്ട് പ്രവർത്തനം"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"പ്രവർത്തനരഹിതമാക്കി"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"പ്രവർത്തനക്ഷമമാക്കി"</string> diff --git a/packages/SettingsLib/res/values-mn/strings.xml b/packages/SettingsLib/res/values-mn/strings.xml index 3dc498946ff0..80743665ca22 100644 --- a/packages/SettingsLib/res/values-mn/strings.xml +++ b/packages/SettingsLib/res/values-mn/strings.xml @@ -545,19 +545,14 @@ <string name="user_need_lock_message" msgid="4311424336209509301">"Та хязгаарлагдсан профайл үүсгэхийн өмнө өөрийн апп-ууд болон хувийн өгөгдлийг хамгаалахын тулд дэлгэцийн түгжээг тохируулах шаардлагатай."</string> <string name="user_set_lock_button" msgid="1427128184982594856">"Түгжээг тохируулах"</string> <string name="user_switch_to_user" msgid="6975428297154968543">"<xliff:g id="USER_NAME">%s</xliff:g> руу сэлгэх"</string> - <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) --> - <skip /> - <!-- no translation found for user_nickname (262624187455825083) --> - <skip /> + <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Шинэ хэрэглэгч үүсгэж байна…"</string> + <string name="user_nickname" msgid="262624187455825083">"Хоч"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Зочин нэмэх"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Зочин хасах"</string> <string name="guest_nickname" msgid="6332276931583337261">"Зочин"</string> - <!-- no translation found for user_image_take_photo (467512954561638530) --> - <skip /> - <!-- no translation found for user_image_choose_photo (1363820919146782908) --> - <skip /> - <!-- no translation found for user_image_photo_selector (433658323306627093) --> - <skip /> + <string name="user_image_take_photo" msgid="467512954561638530">"Зураг авах"</string> + <string name="user_image_choose_photo" msgid="1363820919146782908">"Зураг сонгох"</string> + <string name="user_image_photo_selector" msgid="433658323306627093">"Зураг сонгох"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Төхөөрөмжийн өгөгдмөл"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Идэвхгүй болгосон"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Идэвхжүүлсэн"</string> diff --git a/packages/SettingsLib/res/values-mr/strings.xml b/packages/SettingsLib/res/values-mr/strings.xml index 7e418a48b9aa..7c8dc24bdfd7 100644 --- a/packages/SettingsLib/res/values-mr/strings.xml +++ b/packages/SettingsLib/res/values-mr/strings.xml @@ -25,7 +25,7 @@ <string name="wifi_remembered" msgid="3266709779723179188">"सेव्ह केले"</string> <string name="wifi_disconnected" msgid="7054450256284661757">"डिस्कनेक्ट केले"</string> <string name="wifi_disabled_generic" msgid="2651916945380294607">"अक्षम"</string> - <string name="wifi_disabled_network_failure" msgid="2660396183242399585">"IP कॉन्फिगरेशन अयशस्वी"</string> + <string name="wifi_disabled_network_failure" msgid="2660396183242399585">"IP कॉंफिगरेशन अयशस्वी"</string> <string name="wifi_disabled_by_recommendation_provider" msgid="1302938248432705534">"कमी दर्जाच्या नेटवर्कमुळे कनेक्ट केलेले नाही"</string> <string name="wifi_disabled_wifi_failure" msgid="8819554899148331100">"WiFi कनेक्शन अयशस्वी"</string> <string name="wifi_disabled_password_failure" msgid="6892387079613226738">"प्रमाणीकरण समस्या"</string> @@ -545,19 +545,14 @@ <string name="user_need_lock_message" msgid="4311424336209509301">"तुम्ही एक प्रतिबंधित प्रोफाईल तयार करु शकण्यापूर्वी तुम्हाला तुमचे अॅप्स आणि वैयक्तिक डेटा संरक्षित करण्यासाठी एक स्क्रीन लॉक सेट करण्याची आवश्यकता राहील."</string> <string name="user_set_lock_button" msgid="1427128184982594856">"लॉक सेट करा"</string> <string name="user_switch_to_user" msgid="6975428297154968543">"<xliff:g id="USER_NAME">%s</xliff:g> वर स्विच करा"</string> - <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) --> - <skip /> - <!-- no translation found for user_nickname (262624187455825083) --> - <skip /> + <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"नवीन वापरकर्ता तयार करत आहे…"</string> + <string name="user_nickname" msgid="262624187455825083">"टोपणनाव"</string> <string name="guest_new_guest" msgid="3482026122932643557">"अतिथी जोडा"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"अतिथी काढून टाका"</string> <string name="guest_nickname" msgid="6332276931583337261">"अतिथी"</string> - <!-- no translation found for user_image_take_photo (467512954561638530) --> - <skip /> - <!-- no translation found for user_image_choose_photo (1363820919146782908) --> - <skip /> - <!-- no translation found for user_image_photo_selector (433658323306627093) --> - <skip /> + <string name="user_image_take_photo" msgid="467512954561638530">"फोटो काढा"</string> + <string name="user_image_choose_photo" msgid="1363820919146782908">"इमेज निवडा"</string> + <string name="user_image_photo_selector" msgid="433658323306627093">"फोटो निवडा"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"डिव्हाइस डीफॉल्ट"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"बंद केले आहे"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"सुरू केले आहे"</string> diff --git a/packages/SettingsLib/res/values-ms/strings.xml b/packages/SettingsLib/res/values-ms/strings.xml index 4c23a8472c0e..d988dd01de92 100644 --- a/packages/SettingsLib/res/values-ms/strings.xml +++ b/packages/SettingsLib/res/values-ms/strings.xml @@ -545,19 +545,14 @@ <string name="user_need_lock_message" msgid="4311424336209509301">"Sebelum anda boleh membuat profil yang terhad, anda perlu menyediakan kunci skrin untuk melindungi apl dan data peribadi anda."</string> <string name="user_set_lock_button" msgid="1427128184982594856">"Tetapkan kunci"</string> <string name="user_switch_to_user" msgid="6975428297154968543">"Tukar kepada <xliff:g id="USER_NAME">%s</xliff:g>"</string> - <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) --> - <skip /> - <!-- no translation found for user_nickname (262624187455825083) --> - <skip /> + <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Mencipta pengguna baharu…"</string> + <string name="user_nickname" msgid="262624187455825083">"Nama panggilan"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Tambah tetamu"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Alih keluar tetamu"</string> <string name="guest_nickname" msgid="6332276931583337261">"Tetamu"</string> - <!-- no translation found for user_image_take_photo (467512954561638530) --> - <skip /> - <!-- no translation found for user_image_choose_photo (1363820919146782908) --> - <skip /> - <!-- no translation found for user_image_photo_selector (433658323306627093) --> - <skip /> + <string name="user_image_take_photo" msgid="467512954561638530">"Ambil foto"</string> + <string name="user_image_choose_photo" msgid="1363820919146782908">"Pilih imej"</string> + <string name="user_image_photo_selector" msgid="433658323306627093">"Pilih foto"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Lalai peranti"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Dilumpuhkan"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Didayakan"</string> diff --git a/packages/SettingsLib/res/values-my/strings.xml b/packages/SettingsLib/res/values-my/strings.xml index aa3cf08de29c..5a167439ec82 100644 --- a/packages/SettingsLib/res/values-my/strings.xml +++ b/packages/SettingsLib/res/values-my/strings.xml @@ -545,19 +545,14 @@ <string name="user_need_lock_message" msgid="4311424336209509301">"ကန့်သတ်ကိုယ်ရေးအချက်အလက်တစ်ခုကို မပြုလုပ်မီ သင်၏ အပလီကေးရှင်းများနှင့် ကိုယ်ပိုင်အချက်အလက်များကို ကာကွယ်ရန် မျက်နှာပြင်သော့ချခြင်းကို စီမံရန် လိုအပ်လိမ့်မည်"</string> <string name="user_set_lock_button" msgid="1427128184982594856">"သော့ချရန် သတ်မှတ်ပါ"</string> <string name="user_switch_to_user" msgid="6975428297154968543">"<xliff:g id="USER_NAME">%s</xliff:g> သို့ ပြောင်းရန်"</string> - <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) --> - <skip /> - <!-- no translation found for user_nickname (262624187455825083) --> - <skip /> + <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"အသုံးပြုသူအသစ် ပြုလုပ်နေသည်…"</string> + <string name="user_nickname" msgid="262624187455825083">"နာမည်ပြောင်"</string> <string name="guest_new_guest" msgid="3482026122932643557">"ဧည့်သည့် ထည့်ရန်"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"ဧည့်သည်ကို ဖယ်ထုတ်ရန်"</string> <string name="guest_nickname" msgid="6332276931583337261">"ဧည့်သည်"</string> - <!-- no translation found for user_image_take_photo (467512954561638530) --> - <skip /> - <!-- no translation found for user_image_choose_photo (1363820919146782908) --> - <skip /> - <!-- no translation found for user_image_photo_selector (433658323306627093) --> - <skip /> + <string name="user_image_take_photo" msgid="467512954561638530">"ဓာတ်ပုံရိုက်ရန်"</string> + <string name="user_image_choose_photo" msgid="1363820919146782908">"ပုံရွေးရန်"</string> + <string name="user_image_photo_selector" msgid="433658323306627093">"ဓာတ်ပုံရွေးရန်"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"စက်ပစ္စည်းမူရင်း"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"ပိတ်ထားသည်"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"ဖွင့်ထားသည်"</string> diff --git a/packages/SettingsLib/res/values-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml index 0c5431b6b24e..df96ea66ad25 100644 --- a/packages/SettingsLib/res/values-nb/strings.xml +++ b/packages/SettingsLib/res/values-nb/strings.xml @@ -545,19 +545,14 @@ <string name="user_need_lock_message" msgid="4311424336209509301">"Før du kan opprette en begrenset profil, må du konfigurere skjermlåsen for å beskytte appene og de personlige dataene dine."</string> <string name="user_set_lock_button" msgid="1427128184982594856">"Angi lås"</string> <string name="user_switch_to_user" msgid="6975428297154968543">"Bytt til <xliff:g id="USER_NAME">%s</xliff:g>"</string> - <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) --> - <skip /> - <!-- no translation found for user_nickname (262624187455825083) --> - <skip /> + <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Oppretter en ny bruker …"</string> + <string name="user_nickname" msgid="262624187455825083">"Kallenavn"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Legg til en gjest"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Fjern gjesten"</string> <string name="guest_nickname" msgid="6332276931583337261">"Gjest"</string> - <!-- no translation found for user_image_take_photo (467512954561638530) --> - <skip /> - <!-- no translation found for user_image_choose_photo (1363820919146782908) --> - <skip /> - <!-- no translation found for user_image_photo_selector (433658323306627093) --> - <skip /> + <string name="user_image_take_photo" msgid="467512954561638530">"Ta et bilde"</string> + <string name="user_image_choose_photo" msgid="1363820919146782908">"Velg et bilde"</string> + <string name="user_image_photo_selector" msgid="433658323306627093">"Velg et bilde"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Standard for enheten"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Slått av"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Slått på"</string> diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml index 4c230d30050e..e34732faf95d 100644 --- a/packages/SettingsLib/res/values-ne/strings.xml +++ b/packages/SettingsLib/res/values-ne/strings.xml @@ -89,7 +89,7 @@ <string name="bluetooth_profile_pbap" msgid="7064307749579335765">"सम्पर्क साझेदारी"</string> <string name="bluetooth_profile_pbap_summary" msgid="2955819694801952056">"सम्पर्क साझेदारीका लागि प्रयोग"</string> <string name="bluetooth_profile_pan_nap" msgid="7871974753822470050">"इन्टरनेट जडान साझेदारी गर्दै"</string> - <string name="bluetooth_profile_map" msgid="8907204701162107271">"पाठ सन्देशहरू"</string> + <string name="bluetooth_profile_map" msgid="8907204701162107271">"टेक्स्ट म्यासेजहरू"</string> <string name="bluetooth_profile_sap" msgid="8304170950447934386">"SIM पहुँच"</string> <string name="bluetooth_profile_a2dp_high_quality" msgid="4739440941324792775">"HD अडियो: <xliff:g id="CODEC_NAME">%1$s</xliff:g>"</string> <string name="bluetooth_profile_a2dp_high_quality_unknown_codec" msgid="2477639096903834374">"HD अडियो"</string> @@ -545,19 +545,14 @@ <string name="user_need_lock_message" msgid="4311424336209509301">"निषेधयुक्त प्रोफाइल बनाउनु अघि तपाईँको एप र व्यक्तिगत डेटा सुरक्षा गर्नाका लागि तपाईँले स्क्रिन लक सेटअप गर्नु पर्दछ ।"</string> <string name="user_set_lock_button" msgid="1427128184982594856">"लक सेट गर्नुहोस्"</string> <string name="user_switch_to_user" msgid="6975428297154968543">"प्रयोगकर्ता बदलेर <xliff:g id="USER_NAME">%s</xliff:g> पार्नुहोस्"</string> - <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) --> - <skip /> - <!-- no translation found for user_nickname (262624187455825083) --> - <skip /> + <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"नयाँ प्रयोगकर्ता बनाउँदै…"</string> + <string name="user_nickname" msgid="262624187455825083">"उपनाम"</string> <string name="guest_new_guest" msgid="3482026122932643557">"अतिथि थप्नुहोस्"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"अतिथि हटाउनुहोस्"</string> <string name="guest_nickname" msgid="6332276931583337261">"अतिथि"</string> - <!-- no translation found for user_image_take_photo (467512954561638530) --> - <skip /> - <!-- no translation found for user_image_choose_photo (1363820919146782908) --> - <skip /> - <!-- no translation found for user_image_photo_selector (433658323306627093) --> - <skip /> + <string name="user_image_take_photo" msgid="467512954561638530">"फोटो खिच्नुहोस्"</string> + <string name="user_image_choose_photo" msgid="1363820919146782908">"कुनै फोटो छनौट गर्नुहोस्"</string> + <string name="user_image_photo_selector" msgid="433658323306627093">"फोटो चयन गर्नुहोस्"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"पूर्वनिर्धारित यन्त्र"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"असक्षम पारिएको छ"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"सक्षम पारिएको छ"</string> diff --git a/packages/SettingsLib/res/values-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml index ae734a5b3fc8..729d57bac3b9 100644 --- a/packages/SettingsLib/res/values-nl/strings.xml +++ b/packages/SettingsLib/res/values-nl/strings.xml @@ -545,19 +545,14 @@ <string name="user_need_lock_message" msgid="4311424336209509301">"Voordat je een beperkt profiel kunt maken, moet je een schermvergrendeling instellen om je apps en persoonsgegevens te beschermen."</string> <string name="user_set_lock_button" msgid="1427128184982594856">"Vergrendeling instellen"</string> <string name="user_switch_to_user" msgid="6975428297154968543">"Overschakelen naar <xliff:g id="USER_NAME">%s</xliff:g>"</string> - <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) --> - <skip /> - <!-- no translation found for user_nickname (262624187455825083) --> - <skip /> + <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Nieuwe gebruiker maken…"</string> + <string name="user_nickname" msgid="262624187455825083">"Bijnaam"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Gast toevoegen"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Gast verwijderen"</string> <string name="guest_nickname" msgid="6332276931583337261">"Gast"</string> - <!-- no translation found for user_image_take_photo (467512954561638530) --> - <skip /> - <!-- no translation found for user_image_choose_photo (1363820919146782908) --> - <skip /> - <!-- no translation found for user_image_photo_selector (433658323306627093) --> - <skip /> + <string name="user_image_take_photo" msgid="467512954561638530">"Foto maken"</string> + <string name="user_image_choose_photo" msgid="1363820919146782908">"Afbeelding kiezen"</string> + <string name="user_image_photo_selector" msgid="433658323306627093">"Foto selecteren"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Apparaatstandaard"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Uitgeschakeld"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Ingeschakeld"</string> diff --git a/packages/SettingsLib/res/values-or/strings.xml b/packages/SettingsLib/res/values-or/strings.xml index 18a88faf2fb4..1ce7d56ea6d6 100644 --- a/packages/SettingsLib/res/values-or/strings.xml +++ b/packages/SettingsLib/res/values-or/strings.xml @@ -545,19 +545,14 @@ <string name="user_need_lock_message" msgid="4311424336209509301">"ପ୍ରତିବନ୍ଧିତ ପ୍ରୋଫାଇଲ୍ ତିଆରି କରିବାବେଳେ, ନିଜ ଆପ୍ ଓ ବ୍ୟକ୍ତିଗତ ତଥ୍ୟର ସୁରକ୍ଷା ପାଇଁ ଏକ ସ୍କ୍ରୀନ୍ ଲକ୍ ସେଟ୍ କରନ୍ତୁ।"</string> <string name="user_set_lock_button" msgid="1427128184982594856">"ଲକ୍ ସେଟ୍ କରନ୍ତୁ"</string> <string name="user_switch_to_user" msgid="6975428297154968543">"<xliff:g id="USER_NAME">%s</xliff:g>କୁ ସ୍ୱିଚ୍ କରନ୍ତୁ"</string> - <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) --> - <skip /> - <!-- no translation found for user_nickname (262624187455825083) --> - <skip /> + <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"ନୂଆ ଉପଯୋଗକର୍ତ୍ତା ତିଆରି କରାଯାଉଛି…"</string> + <string name="user_nickname" msgid="262624187455825083">"ଡାକନାମ"</string> <string name="guest_new_guest" msgid="3482026122932643557">"ଅତିଥି ଯୋଗ କରନ୍ତୁ"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"ଅତିଥିଙ୍କୁ କାଢ଼ି ଦିଅନ୍ତୁ"</string> <string name="guest_nickname" msgid="6332276931583337261">"ଅତିଥି"</string> - <!-- no translation found for user_image_take_photo (467512954561638530) --> - <skip /> - <!-- no translation found for user_image_choose_photo (1363820919146782908) --> - <skip /> - <!-- no translation found for user_image_photo_selector (433658323306627093) --> - <skip /> + <string name="user_image_take_photo" msgid="467512954561638530">"ଗୋଟିଏ ଫଟୋ ଉଠାନ୍ତୁ"</string> + <string name="user_image_choose_photo" msgid="1363820919146782908">"ଏକ ଛବି ବାଛନ୍ତୁ"</string> + <string name="user_image_photo_selector" msgid="433658323306627093">"ଫଟୋ ବାଛନ୍ତୁ"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"ଡିଭାଇସ୍ ଡିଫଲ୍ଟ"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"ଅକ୍ଷମ କରାଯାଇଛି"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"ସକ୍ଷମ କରାଯାଇଛି"</string> diff --git a/packages/SettingsLib/res/values-pa/strings.xml b/packages/SettingsLib/res/values-pa/strings.xml index b1bde27fdf17..c122a280097b 100644 --- a/packages/SettingsLib/res/values-pa/strings.xml +++ b/packages/SettingsLib/res/values-pa/strings.xml @@ -545,19 +545,14 @@ <string name="user_need_lock_message" msgid="4311424336209509301">"ਇਸਤੋਂ ਪਹਿਲਾਂ ਕਿ ਤੁਸੀਂ ਇੱਕ ਪ੍ਰਤਿਬੰਧਿਤ ਪ੍ਰੋਫਾਈਲ ਬਣਾ ਸਕੋ, ਤੁਹਾਨੂੰ ਆਪਣੀਆਂ ਐਪਾਂ ਅਤੇ ਨਿੱਜੀ ਡਾਟਾ ਸੁਰੱਖਿਅਤ ਕਰਨ ਲਈ ਇੱਕ ਸਕ੍ਰੀਨ ਲਾਕ ਸੈੱਟ ਅੱਪ ਕਰਨ ਦੀ ਲੋੜ ਹੈ।"</string> <string name="user_set_lock_button" msgid="1427128184982594856">" ਲਾਕ ਸੈੱਟ ਕਰੋ"</string> <string name="user_switch_to_user" msgid="6975428297154968543">"<xliff:g id="USER_NAME">%s</xliff:g> \'ਤੇ ਜਾਓ"</string> - <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) --> - <skip /> - <!-- no translation found for user_nickname (262624187455825083) --> - <skip /> + <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"ਨਵਾਂ ਵਰਤੋਂਕਾਰ ਬਣਾਇਆ ਜਾ ਰਿਹਾ ਹੈ…"</string> + <string name="user_nickname" msgid="262624187455825083">"ਉਪਨਾਮ"</string> <string name="guest_new_guest" msgid="3482026122932643557">"ਮਹਿਮਾਨ ਸ਼ਾਮਲ ਕਰੋ"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"ਮਹਿਮਾਨ ਹਟਾਓ"</string> <string name="guest_nickname" msgid="6332276931583337261">"ਮਹਿਮਾਨ"</string> - <!-- no translation found for user_image_take_photo (467512954561638530) --> - <skip /> - <!-- no translation found for user_image_choose_photo (1363820919146782908) --> - <skip /> - <!-- no translation found for user_image_photo_selector (433658323306627093) --> - <skip /> + <string name="user_image_take_photo" msgid="467512954561638530">"ਇੱਕ ਫ਼ੋਟੋ ਖਿੱਚੋ"</string> + <string name="user_image_choose_photo" msgid="1363820919146782908">"ਕੋਈ ਚਿੱਤਰ ਚੁਣੋ"</string> + <string name="user_image_photo_selector" msgid="433658323306627093">"ਫ਼ੋਟੋ ਚੁਣੋ"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"ਡੀਵਾਈਸ ਪੂਰਵ-ਨਿਰਧਾਰਤ"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"ਬੰਦ ਕੀਤਾ ਗਿਆ"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"ਚਾਲੂ ਕੀਤਾ ਗਿਆ"</string> diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml index 7ea2d0c3e803..112ae476c86e 100644 --- a/packages/SettingsLib/res/values-pl/strings.xml +++ b/packages/SettingsLib/res/values-pl/strings.xml @@ -547,19 +547,14 @@ <string name="user_need_lock_message" msgid="4311424336209509301">"Zanim utworzysz profil z ograniczeniami, musisz skonfigurować ekran blokady, by chronić aplikacje i osobiste dane."</string> <string name="user_set_lock_button" msgid="1427128184982594856">"Ustaw blokadę"</string> <string name="user_switch_to_user" msgid="6975428297154968543">"Przełącz na: <xliff:g id="USER_NAME">%s</xliff:g>"</string> - <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) --> - <skip /> - <!-- no translation found for user_nickname (262624187455825083) --> - <skip /> + <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Tworzę nowego użytkownika…"</string> + <string name="user_nickname" msgid="262624187455825083">"Pseudonim"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Dodaj gościa"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Usuń gościa"</string> <string name="guest_nickname" msgid="6332276931583337261">"Gość"</string> - <!-- no translation found for user_image_take_photo (467512954561638530) --> - <skip /> - <!-- no translation found for user_image_choose_photo (1363820919146782908) --> - <skip /> - <!-- no translation found for user_image_photo_selector (433658323306627093) --> - <skip /> + <string name="user_image_take_photo" msgid="467512954561638530">"Zrób zdjęcie"</string> + <string name="user_image_choose_photo" msgid="1363820919146782908">"Wybierz obraz"</string> + <string name="user_image_photo_selector" msgid="433658323306627093">"Wybierz zdjęcie"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Ustawienie domyślne urządzenia"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Wyłączono"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Włączono"</string> diff --git a/packages/SettingsLib/res/values-pt-rPT/strings.xml b/packages/SettingsLib/res/values-pt-rPT/strings.xml index de536b28aae9..7354835989ce 100644 --- a/packages/SettingsLib/res/values-pt-rPT/strings.xml +++ b/packages/SettingsLib/res/values-pt-rPT/strings.xml @@ -545,19 +545,14 @@ <string name="user_need_lock_message" msgid="4311424336209509301">"Antes de poder criar um perfil restrito, tem de configurar um bloqueio de ecrã para proteger as suas aplicações e dados pessoais."</string> <string name="user_set_lock_button" msgid="1427128184982594856">"Definir bloqueio"</string> <string name="user_switch_to_user" msgid="6975428297154968543">"Mudar para <xliff:g id="USER_NAME">%s</xliff:g>"</string> - <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) --> - <skip /> - <!-- no translation found for user_nickname (262624187455825083) --> - <skip /> + <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"A criar novo utilizador…"</string> + <string name="user_nickname" msgid="262624187455825083">"Pseudónimo"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Adicionar convidado"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Remover convidado"</string> <string name="guest_nickname" msgid="6332276931583337261">"Convidado"</string> - <!-- no translation found for user_image_take_photo (467512954561638530) --> - <skip /> - <!-- no translation found for user_image_choose_photo (1363820919146782908) --> - <skip /> - <!-- no translation found for user_image_photo_selector (433658323306627093) --> - <skip /> + <string name="user_image_take_photo" msgid="467512954561638530">"Tirar uma foto"</string> + <string name="user_image_choose_photo" msgid="1363820919146782908">"Escolher uma imagem"</string> + <string name="user_image_photo_selector" msgid="433658323306627093">"Selecionar foto"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Predefinição do dispositivo"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Desativada"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Ativada"</string> diff --git a/packages/SettingsLib/res/values-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml index 934ef64161bb..32e70da5d216 100644 --- a/packages/SettingsLib/res/values-ro/strings.xml +++ b/packages/SettingsLib/res/values-ro/strings.xml @@ -546,19 +546,14 @@ <string name="user_need_lock_message" msgid="4311424336209509301">"Înainte de a putea crea un profil cu permisiuni limitate, va trebui să configurați blocarea ecranului pentru a vă proteja aplicațiile și datele personale."</string> <string name="user_set_lock_button" msgid="1427128184982594856">"Configurați blocarea"</string> <string name="user_switch_to_user" msgid="6975428297154968543">"Treceți la <xliff:g id="USER_NAME">%s</xliff:g>"</string> - <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) --> - <skip /> - <!-- no translation found for user_nickname (262624187455825083) --> - <skip /> + <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Se creează un utilizator nou…"</string> + <string name="user_nickname" msgid="262624187455825083">"Pseudonim"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Adăugați un invitat"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Ștergeți invitatul"</string> <string name="guest_nickname" msgid="6332276931583337261">"Invitat"</string> - <!-- no translation found for user_image_take_photo (467512954561638530) --> - <skip /> - <!-- no translation found for user_image_choose_photo (1363820919146782908) --> - <skip /> - <!-- no translation found for user_image_photo_selector (433658323306627093) --> - <skip /> + <string name="user_image_take_photo" msgid="467512954561638530">"Faceți o fotografie"</string> + <string name="user_image_choose_photo" msgid="1363820919146782908">"Alegeți o imagine"</string> + <string name="user_image_photo_selector" msgid="433658323306627093">"Selectați fotografia"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Prestabilit pentru dispozitiv"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Dezactivat"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Activat"</string> diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml index e75ee7fd77e1..b04d93bf552c 100644 --- a/packages/SettingsLib/res/values-ru/strings.xml +++ b/packages/SettingsLib/res/values-ru/strings.xml @@ -547,19 +547,14 @@ <string name="user_need_lock_message" msgid="4311424336209509301">"Чтобы создать профиль с ограниченным доступом, необходимо предварительно настроить блокировку экрана для защиты приложений и личных данных"</string> <string name="user_set_lock_button" msgid="1427128184982594856">"Включить блокировку"</string> <string name="user_switch_to_user" msgid="6975428297154968543">"Переключиться на этот аккаунт: <xliff:g id="USER_NAME">%s</xliff:g>"</string> - <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) --> - <skip /> - <!-- no translation found for user_nickname (262624187455825083) --> - <skip /> + <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Создаем нового пользователя…"</string> + <string name="user_nickname" msgid="262624187455825083">"Псевдоним"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Добавить аккаунт гостя"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Удалить аккаунт гостя"</string> <string name="guest_nickname" msgid="6332276931583337261">"Гость"</string> - <!-- no translation found for user_image_take_photo (467512954561638530) --> - <skip /> - <!-- no translation found for user_image_choose_photo (1363820919146782908) --> - <skip /> - <!-- no translation found for user_image_photo_selector (433658323306627093) --> - <skip /> + <string name="user_image_take_photo" msgid="467512954561638530">"Сделать снимок"</string> + <string name="user_image_choose_photo" msgid="1363820919146782908">"Выбрать фото"</string> + <string name="user_image_photo_selector" msgid="433658323306627093">"Выбрать фотографию"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Вариант по умолчанию"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Отключено"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Включено"</string> diff --git a/packages/SettingsLib/res/values-si/strings.xml b/packages/SettingsLib/res/values-si/strings.xml index dc9b70299fbe..86725c26e878 100644 --- a/packages/SettingsLib/res/values-si/strings.xml +++ b/packages/SettingsLib/res/values-si/strings.xml @@ -545,19 +545,14 @@ <string name="user_need_lock_message" msgid="4311424336209509301">"සීමිත පැතිකඩක් නිර්මාණය කිරීමට කලින්. ඔබගේ යෙදුම් සහ පෞද්ගලික දත්ත ආරක්ෂා කිරීමට තිර අගුලක් සැකසිය යුතුයි."</string> <string name="user_set_lock_button" msgid="1427128184982594856">"අගුල සකසන්න"</string> <string name="user_switch_to_user" msgid="6975428297154968543">"<xliff:g id="USER_NAME">%s</xliff:g> වෙත මාරු වන්න"</string> - <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) --> - <skip /> - <!-- no translation found for user_nickname (262624187455825083) --> - <skip /> + <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"නව පරිශීලක තනමින්…"</string> + <string name="user_nickname" msgid="262624187455825083">"අපනාමය"</string> <string name="guest_new_guest" msgid="3482026122932643557">"අමුත්තා එක් කරන්න"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"අමුත්තා ඉවත් කරන්න"</string> <string name="guest_nickname" msgid="6332276931583337261">"අමුත්තා"</string> - <!-- no translation found for user_image_take_photo (467512954561638530) --> - <skip /> - <!-- no translation found for user_image_choose_photo (1363820919146782908) --> - <skip /> - <!-- no translation found for user_image_photo_selector (433658323306627093) --> - <skip /> + <string name="user_image_take_photo" msgid="467512954561638530">"ඡායාරූපයක් ගන්න"</string> + <string name="user_image_choose_photo" msgid="1363820919146782908">"රූපයක් තෝරන්න"</string> + <string name="user_image_photo_selector" msgid="433658323306627093">"ඡායාරූපය තෝරන්න"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"උපාංගයේ පෙරනිමිය"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"අබල කළා"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"සබලයි"</string> diff --git a/packages/SettingsLib/res/values-sk/strings.xml b/packages/SettingsLib/res/values-sk/strings.xml index 203f253019ee..91e9564a8c80 100644 --- a/packages/SettingsLib/res/values-sk/strings.xml +++ b/packages/SettingsLib/res/values-sk/strings.xml @@ -547,19 +547,14 @@ <string name="user_need_lock_message" msgid="4311424336209509301">"Pred vytvorením obmedzeného profilu je nutné najprv nastaviť zámku obrazovky na ochranu aplikácií a osobných údajov."</string> <string name="user_set_lock_button" msgid="1427128184982594856">"Nastaviť uzamknutie"</string> <string name="user_switch_to_user" msgid="6975428297154968543">"Prepnúť na používateľa <xliff:g id="USER_NAME">%s</xliff:g>"</string> - <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) --> - <skip /> - <!-- no translation found for user_nickname (262624187455825083) --> - <skip /> + <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Vytvára sa nový používateľ…"</string> + <string name="user_nickname" msgid="262624187455825083">"Prezývka"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Pridať hosťa"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Odobrať hosťa"</string> <string name="guest_nickname" msgid="6332276931583337261">"Hosť"</string> - <!-- no translation found for user_image_take_photo (467512954561638530) --> - <skip /> - <!-- no translation found for user_image_choose_photo (1363820919146782908) --> - <skip /> - <!-- no translation found for user_image_photo_selector (433658323306627093) --> - <skip /> + <string name="user_image_take_photo" msgid="467512954561638530">"Odfotiť"</string> + <string name="user_image_choose_photo" msgid="1363820919146782908">"Vybrať obrázok"</string> + <string name="user_image_photo_selector" msgid="433658323306627093">"Vybrať fotku"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Predvol. nastavenie zariadenia"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Vypnuté"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Zapnuté"</string> diff --git a/packages/SettingsLib/res/values-sl/strings.xml b/packages/SettingsLib/res/values-sl/strings.xml index 3bfda06f5f49..45ffcde74872 100644 --- a/packages/SettingsLib/res/values-sl/strings.xml +++ b/packages/SettingsLib/res/values-sl/strings.xml @@ -547,19 +547,14 @@ <string name="user_need_lock_message" msgid="4311424336209509301">"Preden lahko ustvarite profil z omejitvami, morate nastaviti zaklepanje zaslona, da zaščitite aplikacije in osebne podatke."</string> <string name="user_set_lock_button" msgid="1427128184982594856">"Nastavi zaklepanje"</string> <string name="user_switch_to_user" msgid="6975428297154968543">"Preklop na račun <xliff:g id="USER_NAME">%s</xliff:g>"</string> - <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) --> - <skip /> - <!-- no translation found for user_nickname (262624187455825083) --> - <skip /> + <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Ustvarjanje novega uporabnika …"</string> + <string name="user_nickname" msgid="262624187455825083">"Vzdevek"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Dodajanje gosta"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Odstranitev gosta"</string> <string name="guest_nickname" msgid="6332276931583337261">"Gost"</string> - <!-- no translation found for user_image_take_photo (467512954561638530) --> - <skip /> - <!-- no translation found for user_image_choose_photo (1363820919146782908) --> - <skip /> - <!-- no translation found for user_image_photo_selector (433658323306627093) --> - <skip /> + <string name="user_image_take_photo" msgid="467512954561638530">"Fotografiranje"</string> + <string name="user_image_choose_photo" msgid="1363820919146782908">"Izberi sliko"</string> + <string name="user_image_photo_selector" msgid="433658323306627093">"Izbira fotografije"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Privzeta nastavitev naprave"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Onemogočeno"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Omogočeno"</string> diff --git a/packages/SettingsLib/res/values-sq/strings.xml b/packages/SettingsLib/res/values-sq/strings.xml index 859cc704800f..c3983636ffe8 100644 --- a/packages/SettingsLib/res/values-sq/strings.xml +++ b/packages/SettingsLib/res/values-sq/strings.xml @@ -545,19 +545,14 @@ <string name="user_need_lock_message" msgid="4311424336209509301">"Para se të mund të krijosh një profil të kufizuar, duhet të konfigurosh një kyçje të ekranit për të mbrojtur aplikacionet dhe të dhënat e tua personale."</string> <string name="user_set_lock_button" msgid="1427128184982594856">"Cakto kyçjen"</string> <string name="user_switch_to_user" msgid="6975428297154968543">"Kalo te <xliff:g id="USER_NAME">%s</xliff:g>"</string> - <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) --> - <skip /> - <!-- no translation found for user_nickname (262624187455825083) --> - <skip /> + <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Po krijohet një përdorues i ri…"</string> + <string name="user_nickname" msgid="262624187455825083">"Pseudonimi"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Shto të ftuar"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Hiq të ftuarin"</string> <string name="guest_nickname" msgid="6332276931583337261">"I ftuar"</string> - <!-- no translation found for user_image_take_photo (467512954561638530) --> - <skip /> - <!-- no translation found for user_image_choose_photo (1363820919146782908) --> - <skip /> - <!-- no translation found for user_image_photo_selector (433658323306627093) --> - <skip /> + <string name="user_image_take_photo" msgid="467512954561638530">"Bëj një fotografi"</string> + <string name="user_image_choose_photo" msgid="1363820919146782908">"Zgjidh një imazh"</string> + <string name="user_image_photo_selector" msgid="433658323306627093">"Zgjidh një fotografi"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Parazgjedhja e pajisjes"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Joaktiv"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Aktiv"</string> diff --git a/packages/SettingsLib/res/values-sr/strings.xml b/packages/SettingsLib/res/values-sr/strings.xml index 6c02c3c1ce6a..bb59bd11f2ba 100644 --- a/packages/SettingsLib/res/values-sr/strings.xml +++ b/packages/SettingsLib/res/values-sr/strings.xml @@ -546,19 +546,14 @@ <string name="user_need_lock_message" msgid="4311424336209509301">"Да бисте могли да направите ограничени профил, треба да подесите закључавање екрана да бисте заштитили апликације и личне податке."</string> <string name="user_set_lock_button" msgid="1427128184982594856">"Подеси закључавање"</string> <string name="user_switch_to_user" msgid="6975428297154968543">"Пређи на корисника <xliff:g id="USER_NAME">%s</xliff:g>"</string> - <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) --> - <skip /> - <!-- no translation found for user_nickname (262624187455825083) --> - <skip /> + <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Прави се нови корисник…"</string> + <string name="user_nickname" msgid="262624187455825083">"Надимак"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Додај госта"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Уклони госта"</string> <string name="guest_nickname" msgid="6332276931583337261">"Гост"</string> - <!-- no translation found for user_image_take_photo (467512954561638530) --> - <skip /> - <!-- no translation found for user_image_choose_photo (1363820919146782908) --> - <skip /> - <!-- no translation found for user_image_photo_selector (433658323306627093) --> - <skip /> + <string name="user_image_take_photo" msgid="467512954561638530">"Сликај"</string> + <string name="user_image_choose_photo" msgid="1363820919146782908">"Одабери слику"</string> + <string name="user_image_photo_selector" msgid="433658323306627093">"Изаберите слику"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Подразумевано за уређај"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Онемогућено"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Омогућено"</string> diff --git a/packages/SettingsLib/res/values-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml index dc2167561709..e7b1482c3452 100644 --- a/packages/SettingsLib/res/values-sv/strings.xml +++ b/packages/SettingsLib/res/values-sv/strings.xml @@ -545,19 +545,14 @@ <string name="user_need_lock_message" msgid="4311424336209509301">"Innan du skapar en begränsad profil måste du konfigurera ett skärmlås för att skydda dina appar och personliga data."</string> <string name="user_set_lock_button" msgid="1427128184982594856">"Konfigurera lås"</string> <string name="user_switch_to_user" msgid="6975428297154968543">"Byt till <xliff:g id="USER_NAME">%s</xliff:g>"</string> - <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) --> - <skip /> - <!-- no translation found for user_nickname (262624187455825083) --> - <skip /> + <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Skapar ny användare …"</string> + <string name="user_nickname" msgid="262624187455825083">"Smeknamn"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Lägg till gäst"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Ta bort gäst"</string> <string name="guest_nickname" msgid="6332276931583337261">"Gäst"</string> - <!-- no translation found for user_image_take_photo (467512954561638530) --> - <skip /> - <!-- no translation found for user_image_choose_photo (1363820919146782908) --> - <skip /> - <!-- no translation found for user_image_photo_selector (433658323306627093) --> - <skip /> + <string name="user_image_take_photo" msgid="467512954561638530">"Ta ett foto"</string> + <string name="user_image_choose_photo" msgid="1363820919146782908">"Välj en bild"</string> + <string name="user_image_photo_selector" msgid="433658323306627093">"Välj foto"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Enhetens standardinställning"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Inaktiverat"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Aktiverat"</string> diff --git a/packages/SettingsLib/res/values-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml index 8f80e558e5d8..1f0d167f6df2 100644 --- a/packages/SettingsLib/res/values-sw/strings.xml +++ b/packages/SettingsLib/res/values-sw/strings.xml @@ -545,19 +545,14 @@ <string name="user_need_lock_message" msgid="4311424336209509301">"Kabla uunde wasifu uliowekekwa vikwazo, utahitajika kuweka skrini iliyofungwa ili kulinda programu zako na data binafsi."</string> <string name="user_set_lock_button" msgid="1427128184982594856">"Weka ufunguo"</string> <string name="user_switch_to_user" msgid="6975428297154968543">"Badili utumie <xliff:g id="USER_NAME">%s</xliff:g>"</string> - <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) --> - <skip /> - <!-- no translation found for user_nickname (262624187455825083) --> - <skip /> + <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Inaweka mtumiaji mpya…"</string> + <string name="user_nickname" msgid="262624187455825083">"Jina wakilishi"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Weka mgeni"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Ondoa mgeni"</string> <string name="guest_nickname" msgid="6332276931583337261">"Mgeni"</string> - <!-- no translation found for user_image_take_photo (467512954561638530) --> - <skip /> - <!-- no translation found for user_image_choose_photo (1363820919146782908) --> - <skip /> - <!-- no translation found for user_image_photo_selector (433658323306627093) --> - <skip /> + <string name="user_image_take_photo" msgid="467512954561638530">"Piga picha"</string> + <string name="user_image_choose_photo" msgid="1363820919146782908">"Chagua picha"</string> + <string name="user_image_photo_selector" msgid="433658323306627093">"Chagua picha"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Hali chaguomsingi ya kifaa"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Imezimwa"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Imewashwa"</string> diff --git a/packages/SettingsLib/res/values-ta/strings.xml b/packages/SettingsLib/res/values-ta/strings.xml index 106942104984..b6d8be8ae54d 100644 --- a/packages/SettingsLib/res/values-ta/strings.xml +++ b/packages/SettingsLib/res/values-ta/strings.xml @@ -545,19 +545,14 @@ <string name="user_need_lock_message" msgid="4311424336209509301">"நீங்கள் வரையறுக்கப்பட்டச் சுயவிவரத்தை உருவாக்குவதற்கு முன்பு, உங்கள் ஆப்ஸ் மற்றும் தனிப்பட்ட தரவைப் பாதுகாக்கும் வகையில் நீங்கள் திரைப் பூட்டை அமைக்க வேண்டும்."</string> <string name="user_set_lock_button" msgid="1427128184982594856">"பூட்டை அமை"</string> <string name="user_switch_to_user" msgid="6975428297154968543">"<xliff:g id="USER_NAME">%s</xliff:g>க்கு மாறு"</string> - <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) --> - <skip /> - <!-- no translation found for user_nickname (262624187455825083) --> - <skip /> + <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"புதிய பயனரை உருவாக்குகிறது…"</string> + <string name="user_nickname" msgid="262624187455825083">"புனைப்பெயர்"</string> <string name="guest_new_guest" msgid="3482026122932643557">"கெஸ்ட்டைச் சேர்"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"கெஸ்ட்டை அகற்று"</string> <string name="guest_nickname" msgid="6332276931583337261">"கெஸ்ட்"</string> - <!-- no translation found for user_image_take_photo (467512954561638530) --> - <skip /> - <!-- no translation found for user_image_choose_photo (1363820919146782908) --> - <skip /> - <!-- no translation found for user_image_photo_selector (433658323306627093) --> - <skip /> + <string name="user_image_take_photo" msgid="467512954561638530">"படமெடுங்கள்"</string> + <string name="user_image_choose_photo" msgid="1363820919146782908">"படத்தைத் தேர்வுசெய்யுங்கள்"</string> + <string name="user_image_photo_selector" msgid="433658323306627093">"படத்தைத் தேர்ந்தெடுங்கள்"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"சாதனத்தின் இயல்புநிலை"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"முடக்கப்பட்டது"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"இயக்கப்பட்டது"</string> diff --git a/packages/SettingsLib/res/values-te/strings.xml b/packages/SettingsLib/res/values-te/strings.xml index 3ad2375a77dc..5a8e734ab878 100644 --- a/packages/SettingsLib/res/values-te/strings.xml +++ b/packages/SettingsLib/res/values-te/strings.xml @@ -545,19 +545,14 @@ <string name="user_need_lock_message" msgid="4311424336209509301">"మీరు పరిమితం చేయబడిన ప్రొఫైల్ను సృష్టించడానికి ముందు, మీ అనువర్తనాలు మరియు వ్యక్తిగత డేటాను రక్షించడానికి స్క్రీన్ లాక్ను సెటప్ చేయాల్సి ఉంటుంది."</string> <string name="user_set_lock_button" msgid="1427128184982594856">"లాక్ను సెట్ చేయి"</string> <string name="user_switch_to_user" msgid="6975428297154968543">"<xliff:g id="USER_NAME">%s</xliff:g>కు మార్చు"</string> - <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) --> - <skip /> - <!-- no translation found for user_nickname (262624187455825083) --> - <skip /> + <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"కొత్త యూజర్ను క్రియేట్ చేస్తోంది…"</string> + <string name="user_nickname" msgid="262624187455825083">"మారుపేరు"</string> <string name="guest_new_guest" msgid="3482026122932643557">"అతిథిని జోడించండి"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"అతిథిని తీసివేయండి"</string> <string name="guest_nickname" msgid="6332276931583337261">"అతిథి"</string> - <!-- no translation found for user_image_take_photo (467512954561638530) --> - <skip /> - <!-- no translation found for user_image_choose_photo (1363820919146782908) --> - <skip /> - <!-- no translation found for user_image_photo_selector (433658323306627093) --> - <skip /> + <string name="user_image_take_photo" msgid="467512954561638530">"ఒక ఫోటో తీయండి"</string> + <string name="user_image_choose_photo" msgid="1363820919146782908">"ఇమేజ్ను ఎంచుకోండి"</string> + <string name="user_image_photo_selector" msgid="433658323306627093">"ఫోటోను ఎంచుకోండి"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"పరికర ఆటోమేటిక్ సెట్టింగ్"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"డిజేబుల్ చేయబడింది"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"ఎనేబుల్ చేయబడింది"</string> diff --git a/packages/SettingsLib/res/values-th/arrays.xml b/packages/SettingsLib/res/values-th/arrays.xml index 58d8a452ddcb..21fe6e4d3c9f 100644 --- a/packages/SettingsLib/res/values-th/arrays.xml +++ b/packages/SettingsLib/res/values-th/arrays.xml @@ -138,15 +138,15 @@ <item msgid="1333279807604675720">"สเตอริโอ"</item> </string-array> <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_titles"> - <item msgid="1241278021345116816">"เพิ่มประสิทธิภาพสำหรับคุณภาพเสียง (990 kbps/909 kbps)"</item> + <item msgid="1241278021345116816">"เพิ่มประสิทธิภาพเพื่อคุณภาพเสียง (990 kbps/909 kbps)"</item> <item msgid="3523665555859696539">"คุณภาพเสียงและการเชื่อมต่อที่สมดุล (660 kbps/606 kbps)"</item> - <item msgid="886408010459747589">"เพิ่มประสิทธิภาพสำหรับคุณภาพการเชื่อมต่อ (330 kbps/303 kbps)"</item> + <item msgid="886408010459747589">"เพิ่มประสิทธิภาพเพื่อคุณภาพการเชื่อมต่อ (330 kbps/303 kbps)"</item> <item msgid="3808414041654351577">"ดีที่สุดเท่าที่ทำได้ (ปรับอัตราบิตอัตโนมัติ)"</item> </string-array> <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_summaries"> - <item msgid="804499336721569838">"เพิ่มประสิทธิภาพสำหรับคุณภาพเสียง"</item> + <item msgid="804499336721569838">"เพิ่มประสิทธิภาพเพื่อคุณภาพเสียง"</item> <item msgid="7451422070435297462">"คุณภาพเสียงและการเชื่อมต่อที่สมดุล"</item> - <item msgid="6173114545795428901">"เพิ่มประสิทธิภาพสำหรับคุณภาพการเชื่อมต่อ"</item> + <item msgid="6173114545795428901">"เพิ่มประสิทธิภาพเพื่อคุณภาพการเชื่อมต่อ"</item> <item msgid="4349908264188040530">"ดีที่สุดเท่าที่ทำได้ (ปรับอัตราบิตอัตโนมัติ)"</item> </string-array> <string-array name="bluetooth_audio_active_device_summaries"> diff --git a/packages/SettingsLib/res/values-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml index cc0ac748d124..2ae4131ba415 100644 --- a/packages/SettingsLib/res/values-th/strings.xml +++ b/packages/SettingsLib/res/values-th/strings.xml @@ -545,19 +545,14 @@ <string name="user_need_lock_message" msgid="4311424336209509301">"ก่อนที่คุณจะสามารถสร้างโปรไฟล์ที่ถูกจำกัดได้ คุณจะต้องตั้งค่าล็อกหน้าจอเพื่อปกป้องแอปและข้อมูลส่วนตัวของคุณ"</string> <string name="user_set_lock_button" msgid="1427128184982594856">"ตั้งค่าล็อก"</string> <string name="user_switch_to_user" msgid="6975428297154968543">"เปลี่ยนเป็น <xliff:g id="USER_NAME">%s</xliff:g>"</string> - <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) --> - <skip /> - <!-- no translation found for user_nickname (262624187455825083) --> - <skip /> + <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"กำลังสร้างผู้ใช้ใหม่…"</string> + <string name="user_nickname" msgid="262624187455825083">"ชื่อเล่น"</string> <string name="guest_new_guest" msgid="3482026122932643557">"เพิ่มผู้เข้าร่วม"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"นำผู้เข้าร่วมออก"</string> <string name="guest_nickname" msgid="6332276931583337261">"ผู้ใช้ชั่วคราว"</string> - <!-- no translation found for user_image_take_photo (467512954561638530) --> - <skip /> - <!-- no translation found for user_image_choose_photo (1363820919146782908) --> - <skip /> - <!-- no translation found for user_image_photo_selector (433658323306627093) --> - <skip /> + <string name="user_image_take_photo" msgid="467512954561638530">"ถ่ายรูป"</string> + <string name="user_image_choose_photo" msgid="1363820919146782908">"เลือกรูปภาพ"</string> + <string name="user_image_photo_selector" msgid="433658323306627093">"เลือกรูปภาพ"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"ค่าเริ่มต้นของอุปกรณ์"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"ปิดใช้"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"เปิดใช้"</string> diff --git a/packages/SettingsLib/res/values-tl/strings.xml b/packages/SettingsLib/res/values-tl/strings.xml index 77ab7cfcaa0d..e9f1da3c5c4b 100644 --- a/packages/SettingsLib/res/values-tl/strings.xml +++ b/packages/SettingsLib/res/values-tl/strings.xml @@ -545,19 +545,14 @@ <string name="user_need_lock_message" msgid="4311424336209509301">"Bago ka makakalikha ng pinaghihigpitang profile, kakailanganin mong mag-set up ng screen lock upang protektahan ang iyong apps at personal na data."</string> <string name="user_set_lock_button" msgid="1427128184982594856">"Itakda ang lock"</string> <string name="user_switch_to_user" msgid="6975428297154968543">"Lumipat sa <xliff:g id="USER_NAME">%s</xliff:g>"</string> - <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) --> - <skip /> - <!-- no translation found for user_nickname (262624187455825083) --> - <skip /> + <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Gumagawa ng bagong user…"</string> + <string name="user_nickname" msgid="262624187455825083">"Nickname"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Magdagdag ng bisita"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Alisin ang bisita"</string> <string name="guest_nickname" msgid="6332276931583337261">"Bisita"</string> - <!-- no translation found for user_image_take_photo (467512954561638530) --> - <skip /> - <!-- no translation found for user_image_choose_photo (1363820919146782908) --> - <skip /> - <!-- no translation found for user_image_photo_selector (433658323306627093) --> - <skip /> + <string name="user_image_take_photo" msgid="467512954561638530">"Kumuha ng larawan"</string> + <string name="user_image_choose_photo" msgid="1363820919146782908">"Pumili ng larawan"</string> + <string name="user_image_photo_selector" msgid="433658323306627093">"Pumili ng larawan"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Default ng device"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Naka-disable"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Na-enable"</string> diff --git a/packages/SettingsLib/res/values-tr/strings.xml b/packages/SettingsLib/res/values-tr/strings.xml index 9d980c424ef9..0502359dc9da 100644 --- a/packages/SettingsLib/res/values-tr/strings.xml +++ b/packages/SettingsLib/res/values-tr/strings.xml @@ -545,19 +545,14 @@ <string name="user_need_lock_message" msgid="4311424336209509301">"Kısıtlanmış bir profil oluşturabilmeniz için uygulamalarınızı ve kişisel verilerinizi korumak üzere bir ekran kilidi oluşturmanız gerekir."</string> <string name="user_set_lock_button" msgid="1427128184982594856">"Kilidi ayarla"</string> <string name="user_switch_to_user" msgid="6975428297154968543">"<xliff:g id="USER_NAME">%s</xliff:g> hesabına geç"</string> - <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) --> - <skip /> - <!-- no translation found for user_nickname (262624187455825083) --> - <skip /> + <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Yeni kullanıcı oluşturuluyor…"</string> + <string name="user_nickname" msgid="262624187455825083">"Takma ad"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Misafir ekle"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Misafir oturumunu kaldır"</string> <string name="guest_nickname" msgid="6332276931583337261">"Misafir"</string> - <!-- no translation found for user_image_take_photo (467512954561638530) --> - <skip /> - <!-- no translation found for user_image_choose_photo (1363820919146782908) --> - <skip /> - <!-- no translation found for user_image_photo_selector (433658323306627093) --> - <skip /> + <string name="user_image_take_photo" msgid="467512954561638530">"Fotoğraf çek"</string> + <string name="user_image_choose_photo" msgid="1363820919146782908">"Resim seç"</string> + <string name="user_image_photo_selector" msgid="433658323306627093">"Fotoğraf seç"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Cihaz varsayılanı"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Devre dışı"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Etkin"</string> diff --git a/packages/SettingsLib/res/values-uk/strings.xml b/packages/SettingsLib/res/values-uk/strings.xml index 9a7b89fc8f58..b68aab46c028 100644 --- a/packages/SettingsLib/res/values-uk/strings.xml +++ b/packages/SettingsLib/res/values-uk/strings.xml @@ -547,19 +547,14 @@ <string name="user_need_lock_message" msgid="4311424336209509301">"Перш ніж створювати обмежений профіль, потрібно налаштувати блокування екрана, щоб захистити свої програми та особисті дані."</string> <string name="user_set_lock_button" msgid="1427128184982594856">"Налаштувати блокування"</string> <string name="user_switch_to_user" msgid="6975428297154968543">"Перейти до користувача <xliff:g id="USER_NAME">%s</xliff:g>"</string> - <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) --> - <skip /> - <!-- no translation found for user_nickname (262624187455825083) --> - <skip /> + <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Створення нового користувача…"</string> + <string name="user_nickname" msgid="262624187455825083">"Псевдонім"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Додати гостя"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Видалити гостя"</string> <string name="guest_nickname" msgid="6332276931583337261">"Гість"</string> - <!-- no translation found for user_image_take_photo (467512954561638530) --> - <skip /> - <!-- no translation found for user_image_choose_photo (1363820919146782908) --> - <skip /> - <!-- no translation found for user_image_photo_selector (433658323306627093) --> - <skip /> + <string name="user_image_take_photo" msgid="467512954561638530">"Зробити фотографію"</string> + <string name="user_image_choose_photo" msgid="1363820919146782908">"Вибрати зображення"</string> + <string name="user_image_photo_selector" msgid="433658323306627093">"Вибрати фотографію"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"За умовчанням для пристрою"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Вимкнено"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Увімкнено"</string> diff --git a/packages/SettingsLib/res/values-ur/strings.xml b/packages/SettingsLib/res/values-ur/strings.xml index 1cc0859971f7..b971aeef9c9b 100644 --- a/packages/SettingsLib/res/values-ur/strings.xml +++ b/packages/SettingsLib/res/values-ur/strings.xml @@ -197,9 +197,9 @@ <string name="category_personal" msgid="6236798763159385225">"ذاتی"</string> <string name="category_work" msgid="4014193632325996115">"دفتر"</string> <string name="development_settings_title" msgid="140296922921597393">"ڈویلپر کے اختیارات"</string> - <string name="development_settings_enable" msgid="4285094651288242183">"ڈیولپر کے اختیارات فعال کریں"</string> + <string name="development_settings_enable" msgid="4285094651288242183">"ڈویلپر کے اختیارات فعال کریں"</string> <string name="development_settings_summary" msgid="8718917813868735095">"ایپ ڈویلپمنٹ کیلئے اختیارات سیٹ کریں"</string> - <string name="development_settings_not_available" msgid="355070198089140951">"اس صارف کیلئے ڈیولپر کے اختیارات دستیاب نہیں ہیں"</string> + <string name="development_settings_not_available" msgid="355070198089140951">"اس صارف کیلئے ڈویلپر کے اختیارات دستیاب نہیں ہیں"</string> <string name="vpn_settings_not_available" msgid="2894137119965668920">"VPN ترتیبات اس صارف کیلئے دستیاب نہیں ہیں"</string> <string name="tethering_settings_not_available" msgid="266821736434699780">"ٹیدرنگ ترتیبات اس صارف کیلئے دستیاب نہیں ہیں"</string> <string name="apn_settings_not_available" msgid="1147111671403342300">"رسائی کی جگہ کے نام کی ترتیبات اس صارف کیلئے دستیاب نہیں ہیں"</string> @@ -545,19 +545,14 @@ <string name="user_need_lock_message" msgid="4311424336209509301">"ایک محدود پروفائل بنانے سے پہلے، آپ کو اپنی ایپس اور ذاتی ڈیٹا کو محفوظ کرنے کیلئے ایک اسکرین لاک سیٹ اپ کرنا ہوگا۔"</string> <string name="user_set_lock_button" msgid="1427128184982594856">"لاک سیٹ کریں"</string> <string name="user_switch_to_user" msgid="6975428297154968543">"<xliff:g id="USER_NAME">%s</xliff:g> پر سوئچ کریں"</string> - <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) --> - <skip /> - <!-- no translation found for user_nickname (262624187455825083) --> - <skip /> + <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"نیا صارف تخلیق کرنا…"</string> + <string name="user_nickname" msgid="262624187455825083">"عرفی نام"</string> <string name="guest_new_guest" msgid="3482026122932643557">"مہمان کو شامل کریں"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"مہمان کو ہٹائیں"</string> <string name="guest_nickname" msgid="6332276931583337261">"مہمان"</string> - <!-- no translation found for user_image_take_photo (467512954561638530) --> - <skip /> - <!-- no translation found for user_image_choose_photo (1363820919146782908) --> - <skip /> - <!-- no translation found for user_image_photo_selector (433658323306627093) --> - <skip /> + <string name="user_image_take_photo" msgid="467512954561638530">"ایک تصویر لیں"</string> + <string name="user_image_choose_photo" msgid="1363820919146782908">"ایک تصویر منتخب کریں"</string> + <string name="user_image_photo_selector" msgid="433658323306627093">"تصویر منتخب کریں"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"آلہ ڈیفالٹ"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"غیر فعال"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"فعال"</string> diff --git a/packages/SettingsLib/res/values-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml index 2ed6ca8e6beb..4d4808f77d04 100644 --- a/packages/SettingsLib/res/values-vi/strings.xml +++ b/packages/SettingsLib/res/values-vi/strings.xml @@ -545,19 +545,14 @@ <string name="user_need_lock_message" msgid="4311424336209509301">"Trước khi bạn có thể tạo tiểu sử bị hạn chế, bạn sẽ cần thiết lập một màn hình khóa để bảo vệ các ứng dụng và dữ liệu cá nhân của bạn."</string> <string name="user_set_lock_button" msgid="1427128184982594856">"Thiết lập khóa"</string> <string name="user_switch_to_user" msgid="6975428297154968543">"Chuyển sang <xliff:g id="USER_NAME">%s</xliff:g>"</string> - <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) --> - <skip /> - <!-- no translation found for user_nickname (262624187455825083) --> - <skip /> + <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Đang tạo người dùng mới…"</string> + <string name="user_nickname" msgid="262624187455825083">"Biệt hiệu"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Thêm khách"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Xóa phiên khách"</string> <string name="guest_nickname" msgid="6332276931583337261">"Khách"</string> - <!-- no translation found for user_image_take_photo (467512954561638530) --> - <skip /> - <!-- no translation found for user_image_choose_photo (1363820919146782908) --> - <skip /> - <!-- no translation found for user_image_photo_selector (433658323306627093) --> - <skip /> + <string name="user_image_take_photo" msgid="467512954561638530">"Chụp ảnh"</string> + <string name="user_image_choose_photo" msgid="1363820919146782908">"Chọn một hình ảnh"</string> + <string name="user_image_photo_selector" msgid="433658323306627093">"Chọn ảnh"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Theo giá trị mặc định của thiết bị"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Đã tắt"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Đã bật"</string> diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml index 024ea790f8f4..b6e8eba2e159 100644 --- a/packages/SettingsLib/res/values-zh-rCN/strings.xml +++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml @@ -545,19 +545,14 @@ <string name="user_need_lock_message" msgid="4311424336209509301">"您需要先设置锁定屏幕来保护您的应用和个人数据,然后才可以创建受限个人资料。"</string> <string name="user_set_lock_button" msgid="1427128184982594856">"设置屏幕锁定方式"</string> <string name="user_switch_to_user" msgid="6975428297154968543">"切换到<xliff:g id="USER_NAME">%s</xliff:g>"</string> - <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) --> - <skip /> - <!-- no translation found for user_nickname (262624187455825083) --> - <skip /> + <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"正在创建新用户…"</string> + <string name="user_nickname" msgid="262624187455825083">"昵称"</string> <string name="guest_new_guest" msgid="3482026122932643557">"添加访客"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"移除访客"</string> <string name="guest_nickname" msgid="6332276931583337261">"访客"</string> - <!-- no translation found for user_image_take_photo (467512954561638530) --> - <skip /> - <!-- no translation found for user_image_choose_photo (1363820919146782908) --> - <skip /> - <!-- no translation found for user_image_photo_selector (433658323306627093) --> - <skip /> + <string name="user_image_take_photo" msgid="467512954561638530">"拍摄照片"</string> + <string name="user_image_choose_photo" msgid="1363820919146782908">"选择图片"</string> + <string name="user_image_photo_selector" msgid="433658323306627093">"选择照片"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"设备默认设置"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"已停用"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"已启用"</string> diff --git a/packages/SettingsLib/res/values-zh-rHK/strings.xml b/packages/SettingsLib/res/values-zh-rHK/strings.xml index 75f050f21fee..07bc88731a71 100644 --- a/packages/SettingsLib/res/values-zh-rHK/strings.xml +++ b/packages/SettingsLib/res/values-zh-rHK/strings.xml @@ -545,19 +545,14 @@ <string name="user_need_lock_message" msgid="4311424336209509301">"建立限制存取的個人檔案前,您必須先設定上鎖畫面來保護您的應用程式和個人資料。"</string> <string name="user_set_lock_button" msgid="1427128184982594856">"設定上鎖畫面"</string> <string name="user_switch_to_user" msgid="6975428297154968543">"切換至<xliff:g id="USER_NAME">%s</xliff:g>"</string> - <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) --> - <skip /> - <!-- no translation found for user_nickname (262624187455825083) --> - <skip /> + <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"正在建立新使用者…"</string> + <string name="user_nickname" msgid="262624187455825083">"暱稱"</string> <string name="guest_new_guest" msgid="3482026122932643557">"新增訪客"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"移除訪客"</string> <string name="guest_nickname" msgid="6332276931583337261">"訪客"</string> - <!-- no translation found for user_image_take_photo (467512954561638530) --> - <skip /> - <!-- no translation found for user_image_choose_photo (1363820919146782908) --> - <skip /> - <!-- no translation found for user_image_photo_selector (433658323306627093) --> - <skip /> + <string name="user_image_take_photo" msgid="467512954561638530">"拍照"</string> + <string name="user_image_choose_photo" msgid="1363820919146782908">"選擇圖片"</string> + <string name="user_image_photo_selector" msgid="433658323306627093">"揀相"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"裝置預設設定"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"已停用"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"已啟用"</string> diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml index 9866b476f310..833a82ff244f 100644 --- a/packages/SettingsLib/res/values-zh-rTW/strings.xml +++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml @@ -545,19 +545,14 @@ <string name="user_need_lock_message" msgid="4311424336209509301">"如要建立設有限制的個人資料,你必須先設定螢幕鎖定來保護你的應用程式和個人資料。"</string> <string name="user_set_lock_button" msgid="1427128184982594856">"設定鎖定"</string> <string name="user_switch_to_user" msgid="6975428297154968543">"切換至<xliff:g id="USER_NAME">%s</xliff:g>"</string> - <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) --> - <skip /> - <!-- no translation found for user_nickname (262624187455825083) --> - <skip /> + <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"正在建立新使用者…"</string> + <string name="user_nickname" msgid="262624187455825083">"暱稱"</string> <string name="guest_new_guest" msgid="3482026122932643557">"新增訪客"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"移除訪客"</string> <string name="guest_nickname" msgid="6332276931583337261">"訪客"</string> - <!-- no translation found for user_image_take_photo (467512954561638530) --> - <skip /> - <!-- no translation found for user_image_choose_photo (1363820919146782908) --> - <skip /> - <!-- no translation found for user_image_photo_selector (433658323306627093) --> - <skip /> + <string name="user_image_take_photo" msgid="467512954561638530">"拍照"</string> + <string name="user_image_choose_photo" msgid="1363820919146782908">"選擇圖片"</string> + <string name="user_image_photo_selector" msgid="433658323306627093">"選取相片"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"裝置預設設定"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"已停用"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"已啟用"</string> diff --git a/packages/SettingsLib/res/values-zu/strings.xml b/packages/SettingsLib/res/values-zu/strings.xml index b1825c49232c..0065eb616d0c 100644 --- a/packages/SettingsLib/res/values-zu/strings.xml +++ b/packages/SettingsLib/res/values-zu/strings.xml @@ -545,19 +545,14 @@ <string name="user_need_lock_message" msgid="4311424336209509301">"Ngaphambi kokuthi ungadala iphrofayela ekhawulelwe, kuzomele usethe ukukhiya isikrini ukuze uvikele izinhlelo zakho zokusebenza nedatha yakho yomuntu siqu."</string> <string name="user_set_lock_button" msgid="1427128184982594856">"Setha ukukhiya"</string> <string name="user_switch_to_user" msgid="6975428297154968543">"Shintshela ku-<xliff:g id="USER_NAME">%s</xliff:g>"</string> - <!-- no translation found for creating_new_user_dialog_message (7232880257538970375) --> - <skip /> - <!-- no translation found for user_nickname (262624187455825083) --> - <skip /> + <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Idala umsebenzisi omusha…"</string> + <string name="user_nickname" msgid="262624187455825083">"Isiteketiso"</string> <string name="guest_new_guest" msgid="3482026122932643557">"Engeza isivakashi"</string> <string name="guest_exit_guest" msgid="5908239569510734136">"Susa isihambeli"</string> <string name="guest_nickname" msgid="6332276931583337261">"Isihambeli"</string> - <!-- no translation found for user_image_take_photo (467512954561638530) --> - <skip /> - <!-- no translation found for user_image_choose_photo (1363820919146782908) --> - <skip /> - <!-- no translation found for user_image_photo_selector (433658323306627093) --> - <skip /> + <string name="user_image_take_photo" msgid="467512954561638530">"Thatha isithombe"</string> + <string name="user_image_choose_photo" msgid="1363820919146782908">"Khetha isithombe"</string> + <string name="user_image_photo_selector" msgid="433658323306627093">"Khetha isithombe"</string> <string name="cached_apps_freezer_device_default" msgid="2616594131750144342">"Idivayisi ezenzakalelayo"</string> <string name="cached_apps_freezer_disabled" msgid="4816382260660472042">"Ikhutshaziwe"</string> <string name="cached_apps_freezer_enabled" msgid="8866703500183051546">"Inikwe amandla"</string> diff --git a/packages/SettingsLib/src/com/android/settingslib/location/SettingsInjector.java b/packages/SettingsLib/src/com/android/settingslib/location/SettingsInjector.java index 450bdb161933..a0c8663a8271 100644 --- a/packages/SettingsLib/src/com/android/settingslib/location/SettingsInjector.java +++ b/packages/SettingsLib/src/com/android/settingslib/location/SettingsInjector.java @@ -129,10 +129,14 @@ public class SettingsInjector { if (Log.isLoggable(TAG, Log.DEBUG)) { Log.d(TAG, "Found services for profile id " + profileId + ": " + resolveInfos); } + + final PackageManager userPackageManager = mContext.createContextAsUser( + userHandle, /* flags */ 0).getPackageManager(); List<InjectedSetting> settings = new ArrayList<InjectedSetting>(resolveInfos.size()); for (ResolveInfo resolveInfo : resolveInfos) { try { - InjectedSetting setting = parseServiceInfo(resolveInfo, userHandle, pm); + InjectedSetting setting = parseServiceInfo(resolveInfo, userHandle, + userPackageManager); if (setting == null) { Log.w(TAG, "Unable to load service info " + resolveInfo); } else { @@ -248,8 +252,7 @@ public class SettingsInjector { + SettingInjectorService.ATTRIBUTES_NAME + " tag"); } - Resources res = pm.getResourcesForApplicationAsUser(si.packageName, - userHandle.getIdentifier()); + Resources res = pm.getResourcesForApplication(si.packageName); return parseAttributes(si.packageName, si.name, userHandle, res, attrs); } catch (PackageManager.NameNotFoundException e) { throw new XmlPullParserException( diff --git a/packages/SettingsProvider/res/values/defaults.xml b/packages/SettingsProvider/res/values/defaults.xml index 51f69a95e163..5ad43e3c6008 100644 --- a/packages/SettingsProvider/res/values/defaults.xml +++ b/packages/SettingsProvider/res/values/defaults.xml @@ -240,4 +240,7 @@ <!-- Default for setting for Settings.Global.HDMI_CONTROL_AUTO_DEVICE_OFF_ENABLED --> <bool name="def_hdmiControlAutoDeviceOff">false</bool> + + <!-- Default for Settings.Secure.ACCESSIBILITY_MAGNIFICATION_CAPABILITY --> + <integer name="def_accessibility_magnification_capabilities">3</integer> </resources> diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java index f1fb527245a1..90bed12cfd6e 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java @@ -749,25 +749,25 @@ class SettingsProtoDumpUtil { Settings.Global.GPU_DEBUG_LAYERS, GlobalSettingsProto.Gpu.DEBUG_LAYERS); dumpSetting(s, p, - Settings.Global.GLOBAL_SETTINGS_ANGLE_DEBUG_PACKAGE, + Settings.Global.ANGLE_DEBUG_PACKAGE, GlobalSettingsProto.Gpu.ANGLE_DEBUG_PACKAGE); dumpSetting(s, p, - Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_ALL_ANGLE, + Settings.Global.ANGLE_GL_DRIVER_ALL_ANGLE, GlobalSettingsProto.Gpu.ANGLE_GL_DRIVER_ALL_ANGLE); dumpSetting(s, p, - Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_SELECTION_PKGS, + Settings.Global.ANGLE_GL_DRIVER_SELECTION_PKGS, GlobalSettingsProto.Gpu.ANGLE_GL_DRIVER_SELECTION_PKGS); dumpSetting(s, p, - Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_SELECTION_VALUES, + Settings.Global.ANGLE_GL_DRIVER_SELECTION_VALUES, GlobalSettingsProto.Gpu.ANGLE_GL_DRIVER_SELECTION_VALUES); dumpSetting(s, p, - Settings.Global.GLOBAL_SETTINGS_ANGLE_ALLOWLIST, + Settings.Global.ANGLE_ALLOWLIST, GlobalSettingsProto.Gpu.ANGLE_ALLOWLIST); dumpSetting(s, p, Settings.Global.ANGLE_EGL_FEATURES, GlobalSettingsProto.Gpu.ANGLE_EGL_FEATURES); dumpSetting(s, p, - Settings.Global.GLOBAL_SETTINGS_SHOW_ANGLE_IN_USE_DIALOG_BOX, + Settings.Global.SHOW_ANGLE_IN_USE_DIALOG_BOX, GlobalSettingsProto.Gpu.SHOW_ANGLE_IN_USE_DIALOG); dumpSetting(s, p, Settings.Global.GPU_DEBUG_LAYER_APP, diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java index 710c016d594c..6dd2936cfb00 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java @@ -25,6 +25,7 @@ import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_2BUTTON_OVE import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL; import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY; +import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_CONTROLLER_NAME; import static com.android.providers.settings.SettingsState.FALLBACK_FILE_SUFFIX; import android.Manifest; @@ -3341,7 +3342,7 @@ public class SettingsProvider extends ContentProvider { } private final class UpgradeController { - private static final int SETTINGS_VERSION = 192; + private static final int SETTINGS_VERSION = 193; private final int mUserId; @@ -4724,6 +4725,35 @@ public class SettingsProvider extends ContentProvider { currentVersion = 192; } + if (currentVersion == 192) { + // Version 192: set the default value for magnification capabilities. If + // magnification is enabled by the user, set it to full-screen, and set a value + // to show a prompt when using the magnification first time after upgrading. + final SettingsState secureSettings = getSecureSettingsLocked(userId); + final Setting magnificationCapabilities = secureSettings.getSettingLocked( + Secure.ACCESSIBILITY_MAGNIFICATION_CAPABILITY); + if (magnificationCapabilities.isNull()) { + secureSettings.insertSettingLocked( + Secure.ACCESSIBILITY_MAGNIFICATION_CAPABILITY, + String.valueOf(getContext().getResources().getInteger( + R.integer.def_accessibility_magnification_capabilities)), + null, true, SettingsState.SYSTEM_PACKAGE_NAME); + + if (isMagnificationSettingsOn(secureSettings)) { + secureSettings.insertSettingLocked( + Secure.ACCESSIBILITY_MAGNIFICATION_CAPABILITY, String.valueOf( + Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN), + null, false /* makeDefault */, + SettingsState.SYSTEM_PACKAGE_NAME); + secureSettings.insertSettingLocked( + Secure.ACCESSIBILITY_SHOW_WINDOW_MAGNIFICATION_PROMPT, "1", + null, false /* makeDefault */, + SettingsState.SYSTEM_PACKAGE_NAME); + } + } + currentVersion = 193; + } + // vXXX: Add new settings above this point. if (currentVersion != newVersion) { @@ -4862,5 +4892,45 @@ public class SettingsProvider extends ContentProvider { } } } + + private boolean isMagnificationSettingsOn(SettingsState secureSettings) { + if ("1".equals(secureSettings.getSettingLocked( + Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED).getValue())) { + return true; + } + + final Set<String> a11yButtonTargets = transformColonDelimitedStringToSet( + secureSettings.getSettingLocked( + Secure.ACCESSIBILITY_BUTTON_TARGETS).getValue()); + if (a11yButtonTargets != null && a11yButtonTargets.contains( + MAGNIFICATION_CONTROLLER_NAME)) { + return true; + } + + final Set<String> a11yShortcutServices = transformColonDelimitedStringToSet( + secureSettings.getSettingLocked( + Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE).getValue()); + if (a11yShortcutServices != null && a11yShortcutServices.contains( + MAGNIFICATION_CONTROLLER_NAME)) { + return true; + } + return false; + } + + @Nullable + private Set<String> transformColonDelimitedStringToSet(String value) { + if (TextUtils.isEmpty(value)) return null; + final TextUtils.SimpleStringSplitter splitter = new TextUtils.SimpleStringSplitter(':'); + splitter.setString(value); + final Set<String> items = new HashSet<>(); + while (splitter.hasNext()) { + final String str = splitter.next(); + if (TextUtils.isEmpty(str)) { + continue; + } + items.add(str); + } + return items; + } } } diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java index 69be14413583..4713243687c4 100644 --- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java +++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java @@ -314,6 +314,7 @@ public class SettingsBackupTest { Settings.Global.KERNEL_CPU_THREAD_READER, Settings.Global.LANG_ID_UPDATE_CONTENT_URL, Settings.Global.LANG_ID_UPDATE_METADATA_URL, + Settings.Global.LATENCY_TRACKER, Settings.Global.LOCATION_BACKGROUND_THROTTLE_INTERVAL_MS, Settings.Global.LOCATION_BACKGROUND_THROTTLE_PROXIMITY_ALERT_INTERVAL_MS, Settings.Global.LOCATION_BACKGROUND_THROTTLE_PACKAGE_WHITELIST, @@ -407,6 +408,7 @@ public class SettingsBackupTest { Settings.Global.PDP_WATCHDOG_MAX_PDP_RESET_FAIL_COUNT, Settings.Global.PDP_WATCHDOG_POLL_INTERVAL_MS, Settings.Global.PDP_WATCHDOG_TRIGGER_PACKET_COUNT, + Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE, Settings.Global.POLICY_CONTROL, Settings.Global.POWER_BUTTON_SUPPRESSION_DELAY_AFTER_GESTURE_WAKE, Settings.Global.POWER_MANAGER_CONSTANTS, @@ -501,11 +503,11 @@ public class SettingsBackupTest { Settings.Global.GPU_DEBUG_APP, Settings.Global.GPU_DEBUG_LAYERS, Settings.Global.GPU_DEBUG_LAYERS_GLES, - Settings.Global.GLOBAL_SETTINGS_ANGLE_DEBUG_PACKAGE, - Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_ALL_ANGLE, - Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_SELECTION_PKGS, - Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_SELECTION_VALUES, - Settings.Global.GLOBAL_SETTINGS_ANGLE_ALLOWLIST, + Settings.Global.ANGLE_DEBUG_PACKAGE, + Settings.Global.ANGLE_GL_DRIVER_ALL_ANGLE, + Settings.Global.ANGLE_GL_DRIVER_SELECTION_PKGS, + Settings.Global.ANGLE_GL_DRIVER_SELECTION_VALUES, + Settings.Global.ANGLE_ALLOWLIST, Settings.Global.ANGLE_EGL_FEATURES, Settings.Global.UPDATABLE_DRIVER_ALL_APPS, Settings.Global.UPDATABLE_DRIVER_PRODUCTION_OPT_IN_APPS, @@ -515,7 +517,7 @@ public class SettingsBackupTest { Settings.Global.UPDATABLE_DRIVER_PRODUCTION_DENYLIST, Settings.Global.UPDATABLE_DRIVER_PRODUCTION_ALLOWLIST, Settings.Global.UPDATABLE_DRIVER_SPHAL_LIBRARIES, - Settings.Global.GLOBAL_SETTINGS_SHOW_ANGLE_IN_USE_DIALOG_BOX, + Settings.Global.SHOW_ANGLE_IN_USE_DIALOG_BOX, Settings.Global.GPU_DEBUG_LAYER_APP, Settings.Global.ENABLE_GNSS_RAW_MEAS_FULL_TRACKING, Settings.Global.INSTALL_CARRIER_APP_NOTIFICATION_PERSISTENT, @@ -747,7 +749,8 @@ public class SettingsBackupTest { Settings.Secure.WINDOW_MAGNIFICATION, Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_MAGNIFICATION_CONTROLLER, Settings.Secure.SUPPRESS_DOZE, - Settings.Secure.REDUCE_BRIGHT_COLORS_ACTIVATED); + Settings.Secure.REDUCE_BRIGHT_COLORS_ACTIVATED, + Settings.Secure.ACCESSIBILITY_SHOW_WINDOW_MAGNIFICATION_PROMPT); @Test public void systemSettingsBackedUpOrDenied() { diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml index bba29dba0827..5f018a0322a3 100644 --- a/packages/Shell/AndroidManifest.xml +++ b/packages/Shell/AndroidManifest.xml @@ -335,6 +335,9 @@ <!-- Permission required for CTS test - android.server.biometrics --> <uses-permission android:name="android.permission.TEST_BIOMETRIC" /> + <!-- Permissions required for CTS test - NotificationManagerTest --> + <uses-permission android:name="android.permission.MANAGE_NOTIFICATION_LISTENERS" /> + <application android:label="@string/app_label" android:theme="@android:style/Theme.DeviceDefault.DayNight" android:defaultToDeviceProtectedStorage="true" diff --git a/packages/Shell/src/com/android/shell/BugreportProgressService.java b/packages/Shell/src/com/android/shell/BugreportProgressService.java index 0eac4add7b09..02751e27874d 100644 --- a/packages/Shell/src/com/android/shell/BugreportProgressService.java +++ b/packages/Shell/src/com/android/shell/BugreportProgressService.java @@ -737,18 +737,20 @@ public class BugreportProgressService extends Service { final Intent infoIntent = new Intent(mContext, BugreportProgressService.class); infoIntent.setAction(INTENT_BUGREPORT_INFO_LAUNCH); infoIntent.putExtra(EXTRA_ID, info.id); + // Simple notification action button clicks are immutable final PendingIntent infoPendingIntent = PendingIntent.getService(mContext, info.id, infoIntent, - PendingIntent.FLAG_UPDATE_CURRENT); + PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE); final Action infoAction = new Action.Builder(null, mContext.getString(R.string.bugreport_info_action), infoPendingIntent).build(); final Intent screenshotIntent = new Intent(mContext, BugreportProgressService.class); screenshotIntent.setAction(INTENT_BUGREPORT_SCREENSHOT); screenshotIntent.putExtra(EXTRA_ID, info.id); + // Simple notification action button clicks are immutable PendingIntent screenshotPendingIntent = mTakingScreenshot ? null : PendingIntent .getService(mContext, info.id, screenshotIntent, - PendingIntent.FLAG_UPDATE_CURRENT); + PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE); final Action screenshotAction = new Action.Builder(null, mContext.getString(R.string.bugreport_screenshot_action), screenshotPendingIntent).build(); diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp index a9a5671d5b03..80a6257fd7a9 100644 --- a/packages/SystemUI/Android.bp +++ b/packages/SystemUI/Android.bp @@ -139,7 +139,9 @@ android_library { "SystemUI-tags", "SystemUI-proto", "metrics-helper-lib", - "androidx.test.rules", "hamcrest-library", + "hamcrest-library", + "androidx.test.rules", + "androidx.test.uiautomator", "mockito-target-extended-minus-junit4", "testables", "truth-prebuilt", diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml index bf9b809e5684..ef8064fe2c6a 100644 --- a/packages/SystemUI/AndroidManifest.xml +++ b/packages/SystemUI/AndroidManifest.xml @@ -573,10 +573,17 @@ </activity> <!-- People Space UI Screen --> - <activity - android:name=".people.PeopleSpaceActivity" + <activity android:name=".people.PeopleSpaceActivity" + android:label="People" + android:icon="@drawable/ic_music_note" android:exported="true" - android:theme="@android:style/Theme.Material.NoActionBar"> + android:enabled="false" + android:theme="@android:style/Theme.Material.NoActionBar" + android:launchMode="singleInstance"> + <intent-filter> + <action android:name="android.intent.action.MAIN"/> + <category android:name="android.intent.category.LAUNCHER"/> + </intent-filter> </activity> <!-- a gallery of delicious treats --> diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml b/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml index bf2963cd0b7f..7e2e36a4907d 100644 --- a/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml +++ b/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml @@ -61,6 +61,28 @@ android:visibility="invisible" /> </FrameLayout> + <FrameLayout + android:id="@+id/new_lockscreen_clock_view" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignParentEnd="true" + android:layout_alignParentTop="true" + android:visibility="gone"> + <com.android.keyguard.GradientTextClock + android:id="@+id/gradient_clock_view" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textSize="100dp" + android:letterSpacing="0.02" + android:lineSpacingMultiplier=".8" + android:includeFontPadding="false" + android:fontFamily="sans-serif" + android:typeface="monospace" + android:format12Hour="hh\nmm" + android:format24Hour="HH\nmm" + android:elegantTextHeight="false" + /> + </FrameLayout> <include layout="@layout/keyguard_status_area" android:id="@+id/keyguard_status_area" android:layout_width="match_parent" diff --git a/packages/SystemUI/res/layout-land/global_screenshot_preview.xml b/packages/SystemUI/res/layout-land/global_screenshot_preview.xml index b1f4cb7d70de..040303a9b963 100644 --- a/packages/SystemUI/res/layout-land/global_screenshot_preview.xml +++ b/packages/SystemUI/res/layout-land/global_screenshot_preview.xml @@ -28,6 +28,6 @@ android:visibility="gone" android:background="@drawable/screenshot_rounded_corners" android:adjustViewBounds="true" - android:contentDescription="@string/screenshot_preview_description" + android:contentDescription="@string/screenshot_edit" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintStart_toStartOf="parent"/>
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/global_screenshot_action_chip.xml b/packages/SystemUI/res/layout/global_screenshot_action_chip.xml index 4b3534b1cbc8..1021186da8b9 100644 --- a/packages/SystemUI/res/layout/global_screenshot_action_chip.xml +++ b/packages/SystemUI/res/layout/global_screenshot_action_chip.xml @@ -19,7 +19,7 @@ android:id="@+id/global_screenshot_action_chip" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_marginEnd="@dimen/screenshot_action_chip_margin_right" + android:layout_marginStart="@dimen/screenshot_action_chip_margin_start" android:paddingVertical="@dimen/screenshot_action_chip_margin_vertical" android:layout_gravity="center" android:gravity="center" diff --git a/packages/SystemUI/res/layout/global_screenshot_preview.xml b/packages/SystemUI/res/layout/global_screenshot_preview.xml index e6295f54fcbe..c745854b1c6c 100644 --- a/packages/SystemUI/res/layout/global_screenshot_preview.xml +++ b/packages/SystemUI/res/layout/global_screenshot_preview.xml @@ -28,6 +28,6 @@ android:visibility="gone" android:background="@drawable/screenshot_rounded_corners" android:adjustViewBounds="true" - android:contentDescription="@string/screenshot_preview_description" + android:contentDescription="@string/screenshot_edit" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintStart_toStartOf="parent"/>
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/global_screenshot_static.xml b/packages/SystemUI/res/layout/global_screenshot_static.xml index 9ec2f20597e7..26edf3afc0c5 100644 --- a/packages/SystemUI/res/layout/global_screenshot_static.xml +++ b/packages/SystemUI/res/layout/global_screenshot_static.xml @@ -51,7 +51,12 @@ <LinearLayout android:id="@+id/global_screenshot_actions" android:layout_width="wrap_content" - android:layout_height="wrap_content"/> + android:layout_height="wrap_content"> + <include layout="@layout/global_screenshot_action_chip" + android:id="@+id/screenshot_share_chip"/> + <include layout="@layout/global_screenshot_action_chip" + android:id="@+id/screenshot_edit_chip"/> + </LinearLayout> </HorizontalScrollView> <include layout="@layout/global_screenshot_preview"/> </androidx.constraintlayout.widget.ConstraintLayout> diff --git a/packages/SystemUI/res/layout/qs_footer_impl.xml b/packages/SystemUI/res/layout/qs_footer_impl.xml index 436188a83d4f..0822947e8b16 100644 --- a/packages/SystemUI/res/layout/qs_footer_impl.xml +++ b/packages/SystemUI/res/layout/qs_footer_impl.xml @@ -16,7 +16,7 @@ --> <!-- Extends FrameLayout --> -<com.android.systemui.qs.QSFooterImpl +<com.android.systemui.qs.QSFooterView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/qs_footer" android:layout_width="match_parent" @@ -130,4 +130,4 @@ </com.android.systemui.statusbar.AlphaOptimizedFrameLayout> </com.android.keyguard.AlphaOptimizedLinearLayout> </LinearLayout> -</com.android.systemui.qs.QSFooterImpl> +</com.android.systemui.qs.QSFooterView> diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml index b50be52c3acc..e4d751ab100d 100644 --- a/packages/SystemUI/res/values-af/strings.xml +++ b/packages/SystemUI/res/values-af/strings.xml @@ -86,6 +86,8 @@ <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Probeer weer skermkiekie neem"</string> <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Kan weens beperkte bergingspasie nie skermkiekie stoor nie"</string> <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Die program of jou organisasie laat nie toe dat skermkiekies geneem word nie"</string> + <!-- no translation found for screenshot_edit (3510496440489019191) --> + <skip /> <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Maak skermkiekie toe"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"Skermkiekievoorskou"</string> <string name="screenrecord_name" msgid="2596401223859996572">"Skermopnemer"</string> @@ -1063,6 +1065,8 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Laai tans aanbevelings"</string> <string name="controls_media_title" msgid="1746947284862928133">"Media"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"Versteek die huidige sessie."</string> + <!-- no translation found for controls_media_active_session (1984383994625845642) --> + <skip /> <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Maak toe"</string> <string name="controls_media_resume" msgid="1933520684481586053">"Hervat"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"Instellings"</string> diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml index 9e8c8acc8d66..8c22e8f17552 100644 --- a/packages/SystemUI/res/values-am/strings.xml +++ b/packages/SystemUI/res/values-am/strings.xml @@ -86,6 +86,8 @@ <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"ቅጽበታዊ ገጽ ዕይታን እንደገና ማንሳት ይሞክሩ"</string> <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"ባለው ውሱን የማከማቻ ቦታ ምክንያት ቅጽበታዊ ገጽ ዕይታን ማስቀመጥ አይችልም"</string> <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"ቅጽበታዊ ገጽ እይታዎችን ማንሳት በመተግበሪያው ወይም በእርስዎ ድርጅት አይፈቀድም"</string> + <!-- no translation found for screenshot_edit (3510496440489019191) --> + <skip /> <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"ቅጽበታዊ ገጽ እይታን አሰናብት"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"የቅጽበታዊ ገጽ ዕይታ ቅድመ-ዕይታ"</string> <string name="screenrecord_name" msgid="2596401223859996572">"የማያ መቅጃ"</string> @@ -1063,6 +1065,8 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"ምክሮችን በመጫን ላይ"</string> <string name="controls_media_title" msgid="1746947284862928133">"ሚዲያ"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"የአሁኑን ክፍለ-ጊዜ ደብቅ።"</string> + <!-- no translation found for controls_media_active_session (1984383994625845642) --> + <skip /> <string name="controls_media_dismiss_button" msgid="9081375542265132213">"አሰናብት"</string> <string name="controls_media_resume" msgid="1933520684481586053">"ከቆመበት ቀጥል"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"ቅንብሮች"</string> diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml index caae9fbdb5b6..2c74a2789b36 100644 --- a/packages/SystemUI/res/values-ar/strings.xml +++ b/packages/SystemUI/res/values-ar/strings.xml @@ -86,6 +86,8 @@ <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"جرّب أخذ لقطة الشاشة مرة أخرى"</string> <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"يتعذر حفظ لقطة الشاشة لأن مساحة التخزين المتاحة محدودة."</string> <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"يحظر التطبيق أو تحظر مؤسستك التقاط لقطات شاشة"</string> + <!-- no translation found for screenshot_edit (3510496440489019191) --> + <skip /> <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"إغلاق لقطة الشاشة"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"معاينة لقطة الشاشة"</string> <string name="screenrecord_name" msgid="2596401223859996572">"مسجّل الشاشة"</string> @@ -1087,6 +1089,8 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"جارٍ تحميل الاقتراحات"</string> <string name="controls_media_title" msgid="1746947284862928133">"الوسائط"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"إخفاء الجلسة الحالية"</string> + <!-- no translation found for controls_media_active_session (1984383994625845642) --> + <skip /> <string name="controls_media_dismiss_button" msgid="9081375542265132213">"إغلاق"</string> <string name="controls_media_resume" msgid="1933520684481586053">"استئناف التشغيل"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"الإعدادات"</string> diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml index f5f109e7d0b7..94cd1a7d7084 100644 --- a/packages/SystemUI/res/values-as/strings.xml +++ b/packages/SystemUI/res/values-as/strings.xml @@ -86,6 +86,8 @@ <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"স্ক্ৰীণশ্বট আকৌ ল\'বলৈ চেষ্টা কৰক"</string> <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"সঞ্চয়াগাৰত সীমিত খালী ঠাই থকাৰ বাবে স্ক্ৰীণশ্বট ছেভ কৰিব পৰা নগ\'ল"</string> <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"এপটোৱে বা আপোনাৰ প্ৰতিষ্ঠানে স্ক্ৰীণশ্বট ল\'বলৈ অনুমতি নিদিয়ে"</string> + <!-- no translation found for screenshot_edit (3510496440489019191) --> + <skip /> <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"স্ক্ৰীনশ্বট অগ্ৰাহ্য কৰক"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"স্ক্ৰীনশ্বটৰ পূৰ্বদৰ্শন"</string> <string name="screenrecord_name" msgid="2596401223859996572">"স্ক্ৰীন ৰেকৰ্ডাৰ"</string> @@ -1063,6 +1065,8 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"চুপাৰিছসমূহ ল’ড কৰি থকা হৈছে"</string> <string name="controls_media_title" msgid="1746947284862928133">"মিডিয়া"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"বৰ্তমানৰ ছেশ্বনটো লুকুৱাওক।"</string> + <!-- no translation found for controls_media_active_session (1984383994625845642) --> + <skip /> <string name="controls_media_dismiss_button" msgid="9081375542265132213">"অগ্ৰাহ্য কৰক"</string> <string name="controls_media_resume" msgid="1933520684481586053">"পুনৰ আৰম্ভ কৰক"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"ছেটিংসমূহ"</string> diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml index 89149f0b5bbb..43aca9a26e11 100644 --- a/packages/SystemUI/res/values-az/strings.xml +++ b/packages/SystemUI/res/values-az/strings.xml @@ -86,6 +86,8 @@ <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Skrinşotu yenidən çəkin"</string> <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Yaddaş ehtiyatının az olması səbəbindən skrinşotu yadda saxlamaq olmur"</string> <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Skrinşot çəkməyə tətbiq və ya təşkilat tərəfindən icazə verilmir"</string> + <!-- no translation found for screenshot_edit (3510496440489019191) --> + <skip /> <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Ekran şəklini ötürün"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"Ekran şəklinə önbaxış"</string> <string name="screenrecord_name" msgid="2596401223859996572">"Ekran Yazıcısı"</string> @@ -1063,6 +1065,8 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Tövsiyələr yüklənir"</string> <string name="controls_media_title" msgid="1746947284862928133">"Media"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"Cari sessiyanı gizlədin."</string> + <!-- no translation found for controls_media_active_session (1984383994625845642) --> + <skip /> <string name="controls_media_dismiss_button" msgid="9081375542265132213">"İmtina edin"</string> <string name="controls_media_resume" msgid="1933520684481586053">"Davam edin"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"Ayarlar"</string> diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml index b3b722c9c05a..3777dfe66ac6 100644 --- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml +++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml @@ -86,6 +86,8 @@ <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Probajte da ponovo napravite snimak ekrana"</string> <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Čuvanje snimka ekrana nije uspelo zbog ograničenog memorijskog prostora"</string> <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Aplikacija ili organizacija ne dozvoljavaju pravljenje snimaka ekrana"</string> + <!-- no translation found for screenshot_edit (3510496440489019191) --> + <skip /> <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Odbacite snimak ekrana"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"Pregled snimka ekrana"</string> <string name="screenrecord_name" msgid="2596401223859996572">"Snimač ekrana"</string> @@ -1069,6 +1071,8 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Učitavaju se preporuke"</string> <string name="controls_media_title" msgid="1746947284862928133">"Mediji"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"Sakrijte aktuelnu sesiju."</string> + <!-- no translation found for controls_media_active_session (1984383994625845642) --> + <skip /> <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Odbaci"</string> <string name="controls_media_resume" msgid="1933520684481586053">"Nastavi"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"Podešavanja"</string> diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml index f68b69cdc845..8a5d42aeb122 100644 --- a/packages/SystemUI/res/values-be/strings.xml +++ b/packages/SystemUI/res/values-be/strings.xml @@ -86,6 +86,8 @@ <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Паспрабуйце зрабіць здымак экрана яшчэ раз"</string> <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Немагчыма захаваць здымак экрана, бо мала месца ў сховішчы"</string> <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Рабіць здымкі экрана не дазваляе праграма ці ваша арганізацыя"</string> + <!-- no translation found for screenshot_edit (3510496440489019191) --> + <skip /> <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Адхіліць здымак экрана"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"Перадпрагляд здымка экрана"</string> <string name="screenrecord_name" msgid="2596401223859996572">"Запіс экрана"</string> @@ -1075,6 +1077,8 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Загружаюцца рэкамендацыі"</string> <string name="controls_media_title" msgid="1746947284862928133">"Мультымедыя"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"Схаваць цяперашні сеанс."</string> + <!-- no translation found for controls_media_active_session (1984383994625845642) --> + <skip /> <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Адхіліць"</string> <string name="controls_media_resume" msgid="1933520684481586053">"Узнавіць"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"Налады"</string> diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml index 7a4b957e5dda..26a313ad0a11 100644 --- a/packages/SystemUI/res/values-bg/strings.xml +++ b/packages/SystemUI/res/values-bg/strings.xml @@ -86,6 +86,7 @@ <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Опитайте да направите екранна снимка отново"</string> <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Екранната снимка не може да се запази поради ограничено място в хранилището"</string> <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Правенето на екранни снимки не е разрешено от приложението или организацията ви"</string> + <string name="screenshot_edit" msgid="3510496440489019191">"Редактиране на екранната снимка"</string> <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Отхвърляне на екранната снимка"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"Визуализация на екранната снимка"</string> <string name="screenrecord_name" msgid="2596401223859996572">"Запис на екрана"</string> @@ -1063,6 +1064,7 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Препоръките се зареждат"</string> <string name="controls_media_title" msgid="1746947284862928133">"Мултимедия"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"Скриване на текущата сесия."</string> + <string name="controls_media_active_session" msgid="1984383994625845642">"Текущата сесия не може да бъде скрита."</string> <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Отхвърляне"</string> <string name="controls_media_resume" msgid="1933520684481586053">"Възобновяване"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"Настройки"</string> diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml index 0b5d65e3e22b..6cdc128bdb32 100644 --- a/packages/SystemUI/res/values-bn/strings.xml +++ b/packages/SystemUI/res/values-bn/strings.xml @@ -86,6 +86,8 @@ <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"আবার স্ক্রিনশট নেওয়ার চেষ্টা করুন"</string> <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"বেশি জায়গা নেই তাই স্ক্রিনশটটি সেভ করা যাবে না৷"</string> <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"এই অ্যাপ বা আপনার প্রতিষ্ঠান স্ক্রিনশট নেওয়ার অনুমতি দেয়নি"</string> + <!-- no translation found for screenshot_edit (3510496440489019191) --> + <skip /> <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"স্ক্রিনশট বাতিল করুন"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"স্ক্রিনশটের প্রিভিউ"</string> <string name="screenrecord_name" msgid="2596401223859996572">"স্ক্রিন রেকর্ডার"</string> @@ -1018,14 +1020,10 @@ <string name="accessibility_control_move_down" msgid="5390922476900974512">"নিচে নামান"</string> <string name="accessibility_control_move_left" msgid="8156206978511401995">"বাঁদিকে সরান"</string> <string name="accessibility_control_move_right" msgid="8926821093629582888">"ডানদিকে সরান"</string> - <!-- no translation found for magnification_mode_switch_description (2698364322069934733) --> - <skip /> - <!-- no translation found for magnification_mode_switch_state_full_screen (2882507327576770574) --> - <skip /> - <!-- no translation found for magnification_mode_switch_state_window (8597100249594076965) --> - <skip /> - <!-- no translation found for magnification_mode_switch_click_label (2786203505805898199) --> - <skip /> + <string name="magnification_mode_switch_description" msgid="2698364322069934733">"বড় করে দেখার সুইচ"</string> + <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"ফুল-স্ক্রিন মোডে দেখুন"</string> + <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"স্ক্রিনের কিছুটা অংশ বড় করুন"</string> + <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"বদল করুন"</string> <string name="quick_controls_title" msgid="6839108006171302273">"ডিভাইস কন্ট্রোল"</string> <string name="quick_controls_subtitle" msgid="1667408093326318053">"আপনার কানেক্ট করা ডিভাইসের জন্য কন্ট্রোল যোগ করুন"</string> <string name="quick_controls_setup_title" msgid="8901436655997849822">"ডিভাইস কন্ট্রোল সেট-আপ করুন"</string> @@ -1067,6 +1065,8 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"সাজেশন লোড করা হচ্ছে"</string> <string name="controls_media_title" msgid="1746947284862928133">"মিডিয়া"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"বর্তমান সেশন লুকান।"</string> + <!-- no translation found for controls_media_active_session (1984383994625845642) --> + <skip /> <string name="controls_media_dismiss_button" msgid="9081375542265132213">"খারিজ করুন"</string> <string name="controls_media_resume" msgid="1933520684481586053">"আবার চালু করুন"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"সেটিংস"</string> diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml index 1d67443e4423..8f622c8510ea 100644 --- a/packages/SystemUI/res/values-bs/strings.xml +++ b/packages/SystemUI/res/values-bs/strings.xml @@ -86,6 +86,8 @@ <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Pokušajte ponovo snimiti ekran"</string> <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Snimak ekrana se ne može sačuvati zbog manjka prostora za pohranu"</string> <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Ova aplikacija ili vaša organizacija ne dozvoljavaju snimanje ekrana"</string> + <!-- no translation found for screenshot_edit (3510496440489019191) --> + <skip /> <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Odbacite snimak ekrana"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"Pregled snimka ekrana"</string> <string name="screenrecord_name" msgid="2596401223859996572">"Snimač ekrana"</string> @@ -1069,6 +1071,8 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Učitavanje preporuka"</string> <string name="controls_media_title" msgid="1746947284862928133">"Mediji"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"Sakrijte trenutnu sesiju."</string> + <!-- no translation found for controls_media_active_session (1984383994625845642) --> + <skip /> <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Odbaci"</string> <string name="controls_media_resume" msgid="1933520684481586053">"Nastavi"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"Postavke"</string> diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml index 5e06f5569bd6..ea7f36ae4b16 100644 --- a/packages/SystemUI/res/values-ca/strings.xml +++ b/packages/SystemUI/res/values-ca/strings.xml @@ -86,6 +86,8 @@ <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Prova de tornar a fer una captura de pantalla"</string> <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"La captura de pantalla no es pot desar perquè no hi ha prou espai d\'emmagatzematge"</string> <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"L\'aplicació o la teva organització no permeten fer captures de pantalla"</string> + <!-- no translation found for screenshot_edit (3510496440489019191) --> + <skip /> <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Ignora la captura de pantalla"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"Previsualització de la captura de pantalla"</string> <string name="screenrecord_name" msgid="2596401223859996572">"Gravació de pantalla"</string> @@ -1063,6 +1065,8 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Carregant les recomanacions"</string> <string name="controls_media_title" msgid="1746947284862928133">"Multimèdia"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"Amaga la sessió actual."</string> + <!-- no translation found for controls_media_active_session (1984383994625845642) --> + <skip /> <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Ignora"</string> <string name="controls_media_resume" msgid="1933520684481586053">"Reprèn"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"Configuració"</string> diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml index 1b03762f8885..d3e3dbf0e749 100644 --- a/packages/SystemUI/res/values-cs/strings.xml +++ b/packages/SystemUI/res/values-cs/strings.xml @@ -86,6 +86,8 @@ <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Zkuste snímek pořídit znovu"</string> <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Snímek obrazovky kvůli nedostatku místa v úložišti nelze uložit"</string> <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Aplikace nebo organizace zakazuje pořizování snímků obrazovky"</string> + <!-- no translation found for screenshot_edit (3510496440489019191) --> + <skip /> <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Zavřít snímek obrazovky"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"Náhled snímku obrazovky"</string> <string name="screenrecord_name" msgid="2596401223859996572">"Rekordér obrazovky"</string> @@ -1075,6 +1077,8 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Načítání doporučení"</string> <string name="controls_media_title" msgid="1746947284862928133">"Média"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"Skrýt aktuální relaci."</string> + <!-- no translation found for controls_media_active_session (1984383994625845642) --> + <skip /> <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Zavřít"</string> <string name="controls_media_resume" msgid="1933520684481586053">"Pokračovat"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"Nastavení"</string> diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml index fce888e2d089..82e8fd1295db 100644 --- a/packages/SystemUI/res/values-da/strings.xml +++ b/packages/SystemUI/res/values-da/strings.xml @@ -86,6 +86,8 @@ <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Prøv at tage et screenshot igen"</string> <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Screenshottet kan ikke gemmes, fordi der er begrænset lagerplads"</string> <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Appen eller din organisation tillader ikke, at du tager screenshots"</string> + <!-- no translation found for screenshot_edit (3510496440489019191) --> + <skip /> <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Luk screenshot"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"Forhåndsvisning af screenshot"</string> <string name="screenrecord_name" msgid="2596401223859996572">"Skærmoptagelse"</string> @@ -1063,6 +1065,8 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Indlæser anbefalinger"</string> <string name="controls_media_title" msgid="1746947284862928133">"Medie"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"Skjul den aktuelle session."</string> + <!-- no translation found for controls_media_active_session (1984383994625845642) --> + <skip /> <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Luk"</string> <string name="controls_media_resume" msgid="1933520684481586053">"Genoptag"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"Indstillinger"</string> diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml index a6d2dfd21e4d..0e3f309ce5d4 100644 --- a/packages/SystemUI/res/values-de/strings.xml +++ b/packages/SystemUI/res/values-de/strings.xml @@ -28,15 +28,15 @@ <string name="battery_low_percent_format" msgid="4276661262843170964">"<xliff:g id="PERCENTAGE">%s</xliff:g> verbleibend"</string> <string name="battery_low_percent_format_hybrid" msgid="3985614339605686167">"Noch <xliff:g id="PERCENTAGE">%1$s</xliff:g> übrig; bei deinem Nutzungsmuster hast du noch ca. <xliff:g id="TIME">%2$s</xliff:g>"</string> <string name="battery_low_percent_format_hybrid_short" msgid="5917433188456218857">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> ausstehend; noch ca. <xliff:g id="TIME">%2$s</xliff:g>"</string> - <string name="battery_low_percent_format_saver_started" msgid="4968468824040940688">"Noch <xliff:g id="PERCENTAGE">%s</xliff:g>. Der Stromsparmodus ist aktiviert."</string> + <string name="battery_low_percent_format_saver_started" msgid="4968468824040940688">"Noch <xliff:g id="PERCENTAGE">%s</xliff:g>. Der Energiesparmodus ist aktiviert."</string> <string name="invalid_charger" msgid="4370074072117767416">"Aufladen über USB nicht möglich. Verwende das mit dem Gerät gelieferte Ladegerät."</string> <string name="invalid_charger_title" msgid="938685362320735167">"Aufladen über USB nicht möglich"</string> <string name="invalid_charger_text" msgid="2339310107232691577">"Verwende das mit dem Gerät gelieferte Ladegerät"</string> <string name="battery_low_why" msgid="2056750982959359863">"Einstellungen"</string> - <string name="battery_saver_confirmation_title" msgid="1234998463717398453">"Stromsparmodus aktivieren?"</string> - <string name="battery_saver_confirmation_title_generic" msgid="2299231884234959849">"Über den Stromsparmodus"</string> + <string name="battery_saver_confirmation_title" msgid="1234998463717398453">"Energiesparmodus aktivieren?"</string> + <string name="battery_saver_confirmation_title_generic" msgid="2299231884234959849">"Über den Energiesparmodus"</string> <string name="battery_saver_confirmation_ok" msgid="5042136476802816494">"Aktivieren"</string> - <string name="battery_saver_start_action" msgid="4553256017945469937">"Stromsparmodus aktivieren"</string> + <string name="battery_saver_start_action" msgid="4553256017945469937">"Energiesparmodus aktivieren"</string> <string name="status_bar_settings_settings_button" msgid="534331565185171556">"Einstellungen"</string> <string name="status_bar_settings_wifi_button" msgid="7243072479837270946">"WLAN"</string> <string name="status_bar_settings_auto_rotation" msgid="8329080442278431708">"Bildschirm automatisch drehen"</string> @@ -86,6 +86,8 @@ <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Versuche noch einmal, den Screenshot zu erstellen"</string> <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Speichern des Screenshots aufgrund von zu wenig Speicher nicht möglich"</string> <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Die App oder deine Organisation lässt das Erstellen von Screenshots nicht zu"</string> + <!-- no translation found for screenshot_edit (3510496440489019191) --> + <skip /> <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Screenshot schließen"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"Screenshotvorschau"</string> <string name="screenrecord_name" msgid="2596401223859996572">"Bildschirmaufzeichnung"</string> @@ -419,7 +421,7 @@ <string name="quick_settings_night_secondary_label_on_at" msgid="3584738542293528235">"An um <xliff:g id="TIME">%s</xliff:g>"</string> <string name="quick_settings_secondary_label_until" msgid="1883981263191927372">"Bis <xliff:g id="TIME">%s</xliff:g>"</string> <string name="quick_settings_ui_mode_night_label" msgid="1398928270610780470">"Dunkles Design"</string> - <string name="quick_settings_dark_mode_secondary_label_battery_saver" msgid="4990712734503013251">"Stromsparmodus"</string> + <string name="quick_settings_dark_mode_secondary_label_battery_saver" msgid="4990712734503013251">"Energiesparmodus"</string> <string name="quick_settings_dark_mode_secondary_label_on_at_sunset" msgid="6017379738102015710">"An bei Sonnenuntergang"</string> <string name="quick_settings_dark_mode_secondary_label_until_sunrise" msgid="4404885070316716472">"Bis Sonnenaufgang"</string> <string name="quick_settings_dark_mode_secondary_label_on_at" msgid="5128758823486361279">"An um <xliff:g id="TIME">%s</xliff:g>"</string> @@ -497,9 +499,9 @@ <string name="user_remove_user_title" msgid="9124124694835811874">"Nutzer entfernen?"</string> <string name="user_remove_user_message" msgid="6702834122128031833">"Alle Apps und Daten dieses Nutzers werden gelöscht."</string> <string name="user_remove_user_remove" msgid="8387386066949061256">"Entfernen"</string> - <string name="battery_saver_notification_title" msgid="8419266546034372562">"Stromsparmodus ist aktiviert"</string> + <string name="battery_saver_notification_title" msgid="8419266546034372562">"Energiesparmodus ist aktiviert"</string> <string name="battery_saver_notification_text" msgid="2617841636449016951">"Reduzierung der Leistung und Hintergrunddaten"</string> - <string name="battery_saver_notification_action_text" msgid="6022091913807026887">"Stromsparmodus deaktivieren"</string> + <string name="battery_saver_notification_action_text" msgid="6022091913807026887">"Energiesparmodus deaktivieren"</string> <string name="media_projection_dialog_text" msgid="1755705274910034772">"Die App \"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>\" erhält Zugriff auf alle Informationen, die auf deinem Bildschirm sichtbar sind oder von deinem Gerät wiedergegeben werden, während du aufnimmst oder streamst. Dazu gehören beispielsweise angezeigte Passwörter und Zahlungsdetails, Fotos, Nachrichten und Audioinhalte."</string> <string name="media_projection_dialog_service_text" msgid="958000992162214611">"Der Anbieter dieser App erhält Zugriff auf alle Informationen, die auf deinem Bildschirm sichtbar sind oder von deinem Gerät wiedergegeben werden, während du aufnimmst oder streamst. Dazu gehören beispielsweise Passwörter, Zahlungsdetails, Fotos, Nachrichten und Audioinhalte."</string> <string name="media_projection_dialog_service_title" msgid="2888507074107884040">"Aufnahme oder Stream starten?"</string> @@ -773,8 +775,8 @@ <item quantity="one">%d Minute</item> </plurals> <string name="battery_panel_title" msgid="5931157246673665963">"Akkunutzung"</string> - <string name="battery_detail_charging_summary" msgid="8821202155297559706">"Der Stromsparmodus ist beim Aufladen nicht verfügbar."</string> - <string name="battery_detail_switch_title" msgid="6940976502957380405">"Stromsparmodus"</string> + <string name="battery_detail_charging_summary" msgid="8821202155297559706">"Der Energiesparmodus ist beim Aufladen nicht verfügbar."</string> + <string name="battery_detail_switch_title" msgid="6940976502957380405">"Energiesparmodus"</string> <string name="battery_detail_switch_summary" msgid="3668748557848025990">"Reduzierung der Leistung und Hintergrunddaten"</string> <string name="keyboard_key_button_template" msgid="8005673627272051429">"Taste <xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="keyboard_key_home" msgid="3734400625170020657">"Pos1"</string> @@ -961,11 +963,11 @@ <string name="slice_permission_checkbox" msgid="4242888137592298523">"<xliff:g id="APP">%1$s</xliff:g> darf Teile aus jeder beliebigen App anzeigen"</string> <string name="slice_permission_allow" msgid="6340449521277951123">"Zulassen"</string> <string name="slice_permission_deny" msgid="6870256451658176895">"Ablehnen"</string> - <string name="auto_saver_title" msgid="6873691178754086596">"Tippen zum Planen des Stromsparmodus"</string> + <string name="auto_saver_title" msgid="6873691178754086596">"Tippen zum Planen des Energiesparmodus"</string> <string name="auto_saver_text" msgid="3214960308353838764">"Aktivieren, wenn der Akku wahrscheinlich nicht mehr lange hält"</string> <string name="no_auto_saver_action" msgid="7467924389609773835">"Nein danke"</string> - <string name="auto_saver_enabled_title" msgid="4294726198280286333">"Geplanter Stromsparmodus aktiviert"</string> - <string name="auto_saver_enabled_text" msgid="7889491183116752719">"Der Stromsparmodus wird bei einem Akkustand von <xliff:g id="PERCENTAGE">%d</xliff:g> %% automatisch aktiviert."</string> + <string name="auto_saver_enabled_title" msgid="4294726198280286333">"Geplanter Energiesparmodus aktiviert"</string> + <string name="auto_saver_enabled_text" msgid="7889491183116752719">"Der Energiesparmodus wird bei einem Akkustand von <xliff:g id="PERCENTAGE">%d</xliff:g> %% automatisch aktiviert."</string> <string name="open_saver_setting_action" msgid="2111461909782935190">"Einstellungen"</string> <string name="auto_saver_okay_action" msgid="7815925750741935386">"Ok"</string> <string name="heap_dump_tile_name" msgid="2464189856478823046">"Dump SysUI Heap"</string> @@ -1063,6 +1065,8 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Empfehlungen werden geladen"</string> <string name="controls_media_title" msgid="1746947284862928133">"Medien"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"Du kannst die aktuelle Sitzung ausblenden."</string> + <!-- no translation found for controls_media_active_session (1984383994625845642) --> + <skip /> <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Ablehnen"</string> <string name="controls_media_resume" msgid="1933520684481586053">"Fortsetzen"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"Einstellungen"</string> diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml index fc4e78b17ddd..645ff081dd72 100644 --- a/packages/SystemUI/res/values-el/strings.xml +++ b/packages/SystemUI/res/values-el/strings.xml @@ -86,6 +86,8 @@ <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Δοκιμάστε να κάνετε ξανά λήψη του στιγμιότυπου οθόνης"</string> <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Αδύνατη η αποθήκευση του στιγμιότυπου οθόνης λόγω περιορισμένου αποθηκευτικού χώρου"</string> <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Η λήψη στιγμιότυπων οθόνης δεν επιτρέπεται από την εφαρμογή ή τον οργανισμό σας"</string> + <!-- no translation found for screenshot_edit (3510496440489019191) --> + <skip /> <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Παράβλεψη στιγμιότυπου οθόνης"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"Προεπισκόπηση στιγμιότυπου οθόνης"</string> <string name="screenrecord_name" msgid="2596401223859996572">"Εγγραφή οθόνης"</string> @@ -1063,6 +1065,8 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Φόρτωση προτάσεων"</string> <string name="controls_media_title" msgid="1746947284862928133">"Μέσα"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"Απόκρυψη της τρέχουσας περιόδου λειτουργίας."</string> + <!-- no translation found for controls_media_active_session (1984383994625845642) --> + <skip /> <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Παράβλεψη"</string> <string name="controls_media_resume" msgid="1933520684481586053">"Συνέχιση"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"Ρυθμίσεις"</string> diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml index 709a506304e3..a743bbe1d58c 100644 --- a/packages/SystemUI/res/values-en-rAU/strings.xml +++ b/packages/SystemUI/res/values-en-rAU/strings.xml @@ -86,6 +86,7 @@ <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Try taking screenshot again"</string> <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Can\'t save screenshot due to limited storage space"</string> <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Taking screenshots isn\'t allowed by the app or your organisation"</string> + <string name="screenshot_edit" msgid="3510496440489019191">"Edit screenshot"</string> <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Dismiss screenshot"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"Screenshot preview"</string> <string name="screenrecord_name" msgid="2596401223859996572">"Screen Recorder"</string> @@ -1063,6 +1064,7 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Loading recommendations"</string> <string name="controls_media_title" msgid="1746947284862928133">"Media"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"Hide the current session."</string> + <string name="controls_media_active_session" msgid="1984383994625845642">"Current session cannot be hidden."</string> <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Dismiss"</string> <string name="controls_media_resume" msgid="1933520684481586053">"Resume"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"Settings"</string> diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml index 79a28442ca18..3fe7a6a04e6b 100644 --- a/packages/SystemUI/res/values-en-rCA/strings.xml +++ b/packages/SystemUI/res/values-en-rCA/strings.xml @@ -86,6 +86,7 @@ <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Try taking screenshot again"</string> <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Can\'t save screenshot due to limited storage space"</string> <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Taking screenshots isn\'t allowed by the app or your organisation"</string> + <string name="screenshot_edit" msgid="3510496440489019191">"Edit screenshot"</string> <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Dismiss screenshot"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"Screenshot preview"</string> <string name="screenrecord_name" msgid="2596401223859996572">"Screen Recorder"</string> @@ -1063,6 +1064,7 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Loading recommendations"</string> <string name="controls_media_title" msgid="1746947284862928133">"Media"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"Hide the current session."</string> + <string name="controls_media_active_session" msgid="1984383994625845642">"Current session cannot be hidden."</string> <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Dismiss"</string> <string name="controls_media_resume" msgid="1933520684481586053">"Resume"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"Settings"</string> diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml index 709a506304e3..a743bbe1d58c 100644 --- a/packages/SystemUI/res/values-en-rGB/strings.xml +++ b/packages/SystemUI/res/values-en-rGB/strings.xml @@ -86,6 +86,7 @@ <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Try taking screenshot again"</string> <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Can\'t save screenshot due to limited storage space"</string> <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Taking screenshots isn\'t allowed by the app or your organisation"</string> + <string name="screenshot_edit" msgid="3510496440489019191">"Edit screenshot"</string> <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Dismiss screenshot"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"Screenshot preview"</string> <string name="screenrecord_name" msgid="2596401223859996572">"Screen Recorder"</string> @@ -1063,6 +1064,7 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Loading recommendations"</string> <string name="controls_media_title" msgid="1746947284862928133">"Media"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"Hide the current session."</string> + <string name="controls_media_active_session" msgid="1984383994625845642">"Current session cannot be hidden."</string> <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Dismiss"</string> <string name="controls_media_resume" msgid="1933520684481586053">"Resume"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"Settings"</string> diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml index 709a506304e3..a743bbe1d58c 100644 --- a/packages/SystemUI/res/values-en-rIN/strings.xml +++ b/packages/SystemUI/res/values-en-rIN/strings.xml @@ -86,6 +86,7 @@ <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Try taking screenshot again"</string> <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Can\'t save screenshot due to limited storage space"</string> <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Taking screenshots isn\'t allowed by the app or your organisation"</string> + <string name="screenshot_edit" msgid="3510496440489019191">"Edit screenshot"</string> <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Dismiss screenshot"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"Screenshot preview"</string> <string name="screenrecord_name" msgid="2596401223859996572">"Screen Recorder"</string> @@ -1063,6 +1064,7 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Loading recommendations"</string> <string name="controls_media_title" msgid="1746947284862928133">"Media"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"Hide the current session."</string> + <string name="controls_media_active_session" msgid="1984383994625845642">"Current session cannot be hidden."</string> <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Dismiss"</string> <string name="controls_media_resume" msgid="1933520684481586053">"Resume"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"Settings"</string> diff --git a/packages/SystemUI/res/values-en-rXC/strings.xml b/packages/SystemUI/res/values-en-rXC/strings.xml index ab6699b87aed..78006fda8683 100644 --- a/packages/SystemUI/res/values-en-rXC/strings.xml +++ b/packages/SystemUI/res/values-en-rXC/strings.xml @@ -86,6 +86,7 @@ <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Try taking screenshot again"</string> <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Can\'t save screenshot due to limited storage space"</string> <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Taking screenshots isn\'t allowed by the app or your organization"</string> + <string name="screenshot_edit" msgid="3510496440489019191">"Edit screenshot"</string> <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Dismiss screenshot"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"Screenshot preview"</string> <string name="screenrecord_name" msgid="2596401223859996572">"Screen Recorder"</string> @@ -1063,6 +1064,7 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Loading recommendations"</string> <string name="controls_media_title" msgid="1746947284862928133">"Media"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"Hide the current session."</string> + <string name="controls_media_active_session" msgid="1984383994625845642">"Current session cannot be hidden."</string> <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Dismiss"</string> <string name="controls_media_resume" msgid="1933520684481586053">"Resume"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"Settings"</string> diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml index a1e21a10d67d..2b5e96f03f96 100644 --- a/packages/SystemUI/res/values-es-rUS/strings.xml +++ b/packages/SystemUI/res/values-es-rUS/strings.xml @@ -86,6 +86,8 @@ <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Vuelve a hacer una captura de pantalla"</string> <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"No se puede guardar la captura de pantalla debido a que no hay suficiente espacio de almacenamiento"</string> <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"La app o tu organización no permiten las capturas de pantalla"</string> + <!-- no translation found for screenshot_edit (3510496440489019191) --> + <skip /> <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Descartar captura de pantalla"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"Vista previa de la captura de pantalla"</string> <string name="screenrecord_name" msgid="2596401223859996572">"Grabadora de pantalla"</string> @@ -1063,6 +1065,8 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Cargando recomendaciones"</string> <string name="controls_media_title" msgid="1746947284862928133">"Contenido multimedia"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"Oculta la sesión actual."</string> + <!-- no translation found for controls_media_active_session (1984383994625845642) --> + <skip /> <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Descartar"</string> <string name="controls_media_resume" msgid="1933520684481586053">"Reanudar"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"Configuración"</string> diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml index 2003a7d09539..b0377fd899e1 100644 --- a/packages/SystemUI/res/values-es/strings.xml +++ b/packages/SystemUI/res/values-es/strings.xml @@ -86,6 +86,8 @@ <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Vuelve a intentar hacer la captura de pantalla"</string> <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"No se puede guardar la captura de pantalla porque no hay espacio de almacenamiento suficiente"</string> <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"La aplicación o tu organización no permiten realizar capturas de pantalla"</string> + <!-- no translation found for screenshot_edit (3510496440489019191) --> + <skip /> <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Cerrar captura de pantalla"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"Vista previa de captura de pantalla"</string> <string name="screenrecord_name" msgid="2596401223859996572">"Grabación de pantalla"</string> @@ -1063,6 +1065,8 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Cargando recomendaciones"</string> <string name="controls_media_title" msgid="1746947284862928133">"Multimedia"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"Ocultar la sesión."</string> + <!-- no translation found for controls_media_active_session (1984383994625845642) --> + <skip /> <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Cerrar"</string> <string name="controls_media_resume" msgid="1933520684481586053">"Reanudar"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"Ajustes"</string> diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml index 286a42b99eea..a22e12bfa11d 100644 --- a/packages/SystemUI/res/values-et/strings.xml +++ b/packages/SystemUI/res/values-et/strings.xml @@ -86,6 +86,8 @@ <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Proovige ekraanipilt uuesti jäädvustada"</string> <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Piiratud salvestusruumi tõttu ei saa ekraanipilti salvestada"</string> <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Rakendus või teie organisatsioon ei luba ekraanipilte jäädvustada"</string> + <!-- no translation found for screenshot_edit (3510496440489019191) --> + <skip /> <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Sule ekraanipilt"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"Ekraanipildi eelvaade"</string> <string name="screenrecord_name" msgid="2596401223859996572">"Ekraanisalvesti"</string> @@ -1021,7 +1023,7 @@ <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Suurenduse lüliti"</string> <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"Kogu ekraanikuva suurendamine"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Ekraanikuva osa suurendamine"</string> - <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Lüliti"</string> + <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Vaheta"</string> <string name="quick_controls_title" msgid="6839108006171302273">"Seadmete juhikud"</string> <string name="quick_controls_subtitle" msgid="1667408093326318053">"Lisage juhtelemendid ühendatud seadmete jaoks"</string> <string name="quick_controls_setup_title" msgid="8901436655997849822">"Seadmete juhtimisvidinate seadistamine"</string> @@ -1063,6 +1065,8 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Soovituste laadimine"</string> <string name="controls_media_title" msgid="1746947284862928133">"Meedia"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"Peidetakse praegune seanss."</string> + <!-- no translation found for controls_media_active_session (1984383994625845642) --> + <skip /> <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Loobu"</string> <string name="controls_media_resume" msgid="1933520684481586053">"Jätka"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"Seaded"</string> diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml index b6beec061d39..f6ca69796d76 100644 --- a/packages/SystemUI/res/values-eu/strings.xml +++ b/packages/SystemUI/res/values-eu/strings.xml @@ -86,6 +86,8 @@ <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Saiatu berriro pantaila-argazkia ateratzen"</string> <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Ezin da gorde pantaila-argazkia ez delako gelditzen tokirik"</string> <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Aplikazioak edo erakundeak ez du onartzen pantaila-argazkiak ateratzea"</string> + <!-- no translation found for screenshot_edit (3510496440489019191) --> + <skip /> <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Baztertu pantaila-argazkia"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"Pantaila-argazkiaren aurrebista"</string> <string name="screenrecord_name" msgid="2596401223859996572">"Pantaila-grabagailua"</string> @@ -1063,6 +1065,8 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Gomendioak kargatzen"</string> <string name="controls_media_title" msgid="1746947284862928133">"Multimedia-edukia"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"Ezkutatu uneko saioa."</string> + <!-- no translation found for controls_media_active_session (1984383994625845642) --> + <skip /> <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Baztertu"</string> <string name="controls_media_resume" msgid="1933520684481586053">"Berrekin"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"Ezarpenak"</string> diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml index 7849ea4c6bc0..4c913f68ff4d 100644 --- a/packages/SystemUI/res/values-fa/strings.xml +++ b/packages/SystemUI/res/values-fa/strings.xml @@ -86,6 +86,8 @@ <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"دوباره نماگرفت بگیرید"</string> <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"به دلیل محدود بودن فضای ذخیرهسازی نمیتوان نماگرفت را ذخیره کرد"</string> <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"برنامه یا سازمان شما اجازه نمیدهند نماگرفت بگیرید."</string> + <!-- no translation found for screenshot_edit (3510496440489019191) --> + <skip /> <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"رد کردن نماگرفت"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"پیشنمایش نماگرفت"</string> <string name="screenrecord_name" msgid="2596401223859996572">"ضبطکننده صفحهنمایش"</string> @@ -142,7 +144,7 @@ <string name="biometric_dialog_empty_space_description" msgid="3330555462071453396">"برای لغو راستیآزمایی ضربه بزنید"</string> <string name="biometric_dialog_face_icon_description_idle" msgid="4351777022315116816">"لطفاً دوباره امتحان کنید"</string> <string name="biometric_dialog_face_icon_description_authenticating" msgid="3401633342366146535">"درحال جستجوی چهره"</string> - <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"چهره احراز هویت شد"</string> + <string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"چهره اصالتسنجی شد"</string> <string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"تأیید شد"</string> <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"برای تکمیل، روی تأیید ضربه بزنید"</string> <string name="biometric_dialog_authenticated" msgid="7337147327545272484">"راستیآزماییشده"</string> @@ -307,8 +309,8 @@ <string name="accessibility_quick_settings_work_mode_changed_on" msgid="1105258550138313384">"حالت کار روشن شد."</string> <string name="accessibility_quick_settings_data_saver_changed_off" msgid="4910847127871603832">"صرفهجویی داده خاموش شد."</string> <string name="accessibility_quick_settings_data_saver_changed_on" msgid="6370606590802623078">"صرفهجویی داده روشن شد."</string> - <string name="accessibility_quick_settings_sensor_privacy_changed_off" msgid="7608378211873807353">"«حریم خصوصی حسگر» خاموش است."</string> - <string name="accessibility_quick_settings_sensor_privacy_changed_on" msgid="4267393685085328801">"«حریم خصوصی حسگر» روشن است."</string> + <string name="accessibility_quick_settings_sensor_privacy_changed_off" msgid="7608378211873807353">"«حریمخصوصی حسگر» خاموش است."</string> + <string name="accessibility_quick_settings_sensor_privacy_changed_on" msgid="4267393685085328801">"«حریمخصوصی حسگر» روشن است."</string> <string name="accessibility_brightness" msgid="5391187016177823721">"روشنایی نمایشگر"</string> <string name="accessibility_ambient_display_charging" msgid="7725523068728128968">"درحال شارژ شدن"</string> <string name="data_usage_disabled_dialog_3g_title" msgid="5716594205739750015">"داده 2G-3G موقتاً متوقف شده است"</string> @@ -1063,6 +1065,8 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"درحال بار کردن توصیهها"</string> <string name="controls_media_title" msgid="1746947284862928133">"رسانه"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"جلسه فعلی پنهان شود."</string> + <!-- no translation found for controls_media_active_session (1984383994625845642) --> + <skip /> <string name="controls_media_dismiss_button" msgid="9081375542265132213">"رد کردن"</string> <string name="controls_media_resume" msgid="1933520684481586053">"ازسرگیری"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"تنظیمات"</string> diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml index 3e5afc784a9c..0ab407adf99b 100644 --- a/packages/SystemUI/res/values-fi/strings.xml +++ b/packages/SystemUI/res/values-fi/strings.xml @@ -86,6 +86,8 @@ <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Yritä ottaa kuvakaappaus uudelleen."</string> <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Kuvakaappauksen tallennus epäonnistui, sillä tallennustilaa ei ole riittävästi"</string> <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Sovellus tai organisaatio ei salli kuvakaappauksien tallentamista."</string> + <!-- no translation found for screenshot_edit (3510496440489019191) --> + <skip /> <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Hylkää kuvakaappaus"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"Kuvakaappauksen esikatselu"</string> <string name="screenrecord_name" msgid="2596401223859996572">"Näytön tallentaja"</string> @@ -1063,6 +1065,8 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Ladataan suosituksia"</string> <string name="controls_media_title" msgid="1746947284862928133">"Media"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"Piilota nykyinen käyttökerta."</string> + <!-- no translation found for controls_media_active_session (1984383994625845642) --> + <skip /> <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Ohita"</string> <string name="controls_media_resume" msgid="1933520684481586053">"Jatka"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"Asetukset"</string> diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml index 250183e60915..1b7a308f31ca 100644 --- a/packages/SystemUI/res/values-fr-rCA/strings.xml +++ b/packages/SystemUI/res/values-fr-rCA/strings.xml @@ -86,6 +86,8 @@ <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Essayez de faire une autre capture d\'écran"</string> <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Impossible d\'enregistrer la capture d\'écran, car l\'espace de stockage est limité"</string> <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"L\'application ou votre organisation n\'autorise pas les saisies d\'écran"</string> + <!-- no translation found for screenshot_edit (3510496440489019191) --> + <skip /> <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Fermer la capture d\'écran"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"Aperçu de la capture d\'écran"</string> <string name="screenrecord_name" msgid="2596401223859996572">"Enregistreur d\'écran"</string> @@ -1063,6 +1065,8 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Chargement des recommandations…"</string> <string name="controls_media_title" msgid="1746947284862928133">"Commandes multimédias"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"Masquer la session en cours."</string> + <!-- no translation found for controls_media_active_session (1984383994625845642) --> + <skip /> <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Fermer"</string> <string name="controls_media_resume" msgid="1933520684481586053">"Reprendre"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"Paramètres"</string> diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml index 2a4c95af7c46..74a68618fb5d 100644 --- a/packages/SystemUI/res/values-fr/strings.xml +++ b/packages/SystemUI/res/values-fr/strings.xml @@ -86,6 +86,8 @@ <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Essayez de nouveau de faire une capture d\'écran"</string> <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Impossible d\'enregistrer la capture d\'écran, car l\'espace de stockage est limité"</string> <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Les captures d\'écran ne sont pas autorisées par l\'application ni par votre organisation"</string> + <!-- no translation found for screenshot_edit (3510496440489019191) --> + <skip /> <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Fermer la capture d\'écran"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"Aperçu de la capture d\'écran"</string> <string name="screenrecord_name" msgid="2596401223859996572">"Enregistreur d\'écran"</string> @@ -1063,6 +1065,8 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Chargement des recommandations"</string> <string name="controls_media_title" msgid="1746947284862928133">"Multimédia"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"Masquer la session en cours."</string> + <!-- no translation found for controls_media_active_session (1984383994625845642) --> + <skip /> <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Fermer"</string> <string name="controls_media_resume" msgid="1933520684481586053">"Reprendre"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"Paramètres"</string> diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml index fedbe2cf5a9a..26cf07a0dca0 100644 --- a/packages/SystemUI/res/values-gl/strings.xml +++ b/packages/SystemUI/res/values-gl/strings.xml @@ -86,6 +86,8 @@ <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Volve tentar crear unha captura de pantalla"</string> <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Non se puido gardar a captura de pantalla porque o espazo de almacenamento é limitado"</string> <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"A aplicación ou a túa organización non permite realizar capturas de pantalla"</string> + <!-- no translation found for screenshot_edit (3510496440489019191) --> + <skip /> <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Ignora a captura de pantalla"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"Vista previa da captura de pantalla"</string> <string name="screenrecord_name" msgid="2596401223859996572">"Gravadora da pantalla"</string> @@ -1018,14 +1020,10 @@ <string name="accessibility_control_move_down" msgid="5390922476900974512">"Mover cara abaixo"</string> <string name="accessibility_control_move_left" msgid="8156206978511401995">"Mover cara á esquerda"</string> <string name="accessibility_control_move_right" msgid="8926821093629582888">"Mover cara á dereita"</string> - <!-- no translation found for magnification_mode_switch_description (2698364322069934733) --> - <skip /> - <!-- no translation found for magnification_mode_switch_state_full_screen (2882507327576770574) --> - <skip /> - <!-- no translation found for magnification_mode_switch_state_window (8597100249594076965) --> - <skip /> - <!-- no translation found for magnification_mode_switch_click_label (2786203505805898199) --> - <skip /> + <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Interruptor do modo de ampliación"</string> + <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"Amplía toda a pantalla"</string> + <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Amplía parte da pantalla"</string> + <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Cambiar"</string> <string name="quick_controls_title" msgid="6839108006171302273">"Control de dispositivos"</string> <string name="quick_controls_subtitle" msgid="1667408093326318053">"Engade controis para os dispositivos conectados"</string> <string name="quick_controls_setup_title" msgid="8901436655997849822">"Configurar o control de dispositivos"</string> @@ -1067,6 +1065,8 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Cargando recomendacións"</string> <string name="controls_media_title" msgid="1746947284862928133">"Contido multimedia"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"Oculta a sesión actual."</string> + <!-- no translation found for controls_media_active_session (1984383994625845642) --> + <skip /> <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Ignorar"</string> <string name="controls_media_resume" msgid="1933520684481586053">"Retomar"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"Configuración"</string> diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml index d9c5b72be3b2..acaa4420cb56 100644 --- a/packages/SystemUI/res/values-gu/strings.xml +++ b/packages/SystemUI/res/values-gu/strings.xml @@ -86,6 +86,8 @@ <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"ફરીથી સ્ક્રીનશૉટ લેવાનો પ્રયાસ કરો"</string> <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"મર્યાદિત સ્ટોરેજ સ્પેસને કારણે સ્ક્રીનશૉટ સાચવી શકાતો નથી"</string> <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"ઍપ્લિકેશન કે તમારી સંસ્થા દ્વારા સ્ક્રીનશૉટ લેવાની મંજૂરી નથી"</string> + <!-- no translation found for screenshot_edit (3510496440489019191) --> + <skip /> <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"સ્ક્રીનશૉટ છોડી દો"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"સ્ક્રીનશૉટનો પ્રીવ્યૂ"</string> <string name="screenrecord_name" msgid="2596401223859996572">"સ્ક્રીન રેકૉર્ડર"</string> @@ -1018,14 +1020,10 @@ <string name="accessibility_control_move_down" msgid="5390922476900974512">"નીચે ખસેડો"</string> <string name="accessibility_control_move_left" msgid="8156206978511401995">"ડાબી બાજુ ખસેડો"</string> <string name="accessibility_control_move_right" msgid="8926821093629582888">"જમણી બાજુ ખસેડો"</string> - <!-- no translation found for magnification_mode_switch_description (2698364322069934733) --> - <skip /> - <!-- no translation found for magnification_mode_switch_state_full_screen (2882507327576770574) --> - <skip /> - <!-- no translation found for magnification_mode_switch_state_window (8597100249594076965) --> - <skip /> - <!-- no translation found for magnification_mode_switch_click_label (2786203505805898199) --> - <skip /> + <string name="magnification_mode_switch_description" msgid="2698364322069934733">"મોટું કરવાની સુવિધાવાળી સ્વિચ"</string> + <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"સંપૂર્ણ સ્ક્રીન મોટી કરો"</string> + <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"સ્ક્રીનનો કોઈ ભાગ મોટો કરો"</string> + <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"સ્વિચ"</string> <string name="quick_controls_title" msgid="6839108006171302273">"ડિવાઇસનાં નિયંત્રણો"</string> <string name="quick_controls_subtitle" msgid="1667408093326318053">"તમારા કનેક્ટ કરેલા ડિવાઇસ માટે નિયંત્રણો ઉમેરો"</string> <string name="quick_controls_setup_title" msgid="8901436655997849822">"ડિવાઇસનાં નિયંત્રણો સેટઅપ કરો"</string> @@ -1067,6 +1065,8 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"સુઝાવ લોડ કરી રહ્યાં છીએ"</string> <string name="controls_media_title" msgid="1746947284862928133">"મીડિયા"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"હાલનું સત્ર છુપાવો."</string> + <!-- no translation found for controls_media_active_session (1984383994625845642) --> + <skip /> <string name="controls_media_dismiss_button" msgid="9081375542265132213">"છોડી દો"</string> <string name="controls_media_resume" msgid="1933520684481586053">"ફરી શરૂ કરો"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"સેટિંગ"</string> diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml index 181ae74e20cc..d211dce96944 100644 --- a/packages/SystemUI/res/values-hi/strings.xml +++ b/packages/SystemUI/res/values-hi/strings.xml @@ -86,6 +86,8 @@ <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"स्क्रीनशॉट दोबारा लेने की कोशिश करें"</string> <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"मेमोरी कम होने की वजह से स्क्रीनशॉट सेव नहीं किया जा सका"</string> <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"ऐप्लिकेशन या आपका संगठन स्क्रीनशॉट लेने की अनुमति नहीं देता"</string> + <!-- no translation found for screenshot_edit (3510496440489019191) --> + <skip /> <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"स्क्रीनशॉट खारिज करें"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"स्क्रीनशॉट की झलक"</string> <string name="screenrecord_name" msgid="2596401223859996572">"स्क्रीन रिकॉर्डर"</string> @@ -1065,6 +1067,8 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"सुझाव लोड हो रहे हैं"</string> <string name="controls_media_title" msgid="1746947284862928133">"मीडिया"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"इस मीडिया सेशन को छिपाएं."</string> + <!-- no translation found for controls_media_active_session (1984383994625845642) --> + <skip /> <string name="controls_media_dismiss_button" msgid="9081375542265132213">"खारिज करें"</string> <string name="controls_media_resume" msgid="1933520684481586053">"फिर से शुरू करें"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"सेटिंग"</string> diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml index f8318d5ac4f8..b5671bf7abe0 100644 --- a/packages/SystemUI/res/values-hr/strings.xml +++ b/packages/SystemUI/res/values-hr/strings.xml @@ -86,6 +86,8 @@ <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Pokušajte ponovo napraviti snimku zaslona"</string> <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Zaslon nije snimljen zbog ograničenog prostora za pohranu"</string> <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Aplikacija ili vaša organizacija ne dopuštaju snimanje zaslona"</string> + <!-- no translation found for screenshot_edit (3510496440489019191) --> + <skip /> <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Odbacivanje snimke zaslona"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"Pregled snimke zaslona"</string> <string name="screenrecord_name" msgid="2596401223859996572">"Snimač zaslona"</string> @@ -1026,7 +1028,7 @@ <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Prebacivanje povećavanja"</string> <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"Povećaj cijeli zaslon"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Povećaj dio zaslona"</string> - <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Prebaci"</string> + <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Prebacivanje"</string> <string name="quick_controls_title" msgid="6839108006171302273">"Kontrole uređaja"</string> <string name="quick_controls_subtitle" msgid="1667408093326318053">"Dodavanje kontrola za povezane uređaje"</string> <string name="quick_controls_setup_title" msgid="8901436655997849822">"Postavljanje kontrola uređaja"</string> @@ -1069,6 +1071,8 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Učitavanje preporuka"</string> <string name="controls_media_title" msgid="1746947284862928133">"Mediji"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"Sakrij trenutačnu sesiju."</string> + <!-- no translation found for controls_media_active_session (1984383994625845642) --> + <skip /> <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Odbaci"</string> <string name="controls_media_resume" msgid="1933520684481586053">"Nastavi"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"Postavke"</string> diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml index 690508a56513..291976e1f77c 100644 --- a/packages/SystemUI/res/values-hu/strings.xml +++ b/packages/SystemUI/res/values-hu/strings.xml @@ -86,6 +86,8 @@ <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Próbálja meg újra elkészíteni a képernyőképet"</string> <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Nem menthet képernyőképet, mert kevés a tárhely"</string> <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Az alkalmazás vagy az Ön szervezete nem engedélyezi képernyőkép készítését"</string> + <!-- no translation found for screenshot_edit (3510496440489019191) --> + <skip /> <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Képernyőkép elvetése"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"Képernyőkép előnézete"</string> <string name="screenrecord_name" msgid="2596401223859996572">"Képernyőrögzítő"</string> @@ -1063,6 +1065,8 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Javaslatok betöltése…"</string> <string name="controls_media_title" msgid="1746947284862928133">"Média"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"Jelenlegi munkamenet elrejtése."</string> + <!-- no translation found for controls_media_active_session (1984383994625845642) --> + <skip /> <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Elvetés"</string> <string name="controls_media_resume" msgid="1933520684481586053">"Folytatás"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"Beállítások"</string> diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml index 0a966a1a905f..1d8019206cb5 100644 --- a/packages/SystemUI/res/values-hy/strings.xml +++ b/packages/SystemUI/res/values-hy/strings.xml @@ -86,6 +86,7 @@ <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Փորձեք նորից"</string> <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Չհաջողվեց պահել սքրինշոթը անբավարար հիշողության պատճառով"</string> <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Հավելվածը կամ ձեր կազմակերպությունը չի թույլատրում սքրինշոթի ստացումը"</string> + <string name="screenshot_edit" msgid="3510496440489019191">"Փոփոխել սքրինշոթը"</string> <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Փակել սքրինշոթը"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"Սքրինշոթի նախադիտում"</string> <string name="screenrecord_name" msgid="2596401223859996572">"Էկրանի տեսագրիչ"</string> @@ -1063,6 +1064,7 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Բեռնման խորհուրդներ"</string> <string name="controls_media_title" msgid="1746947284862928133">"Մեդիա"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"Թաքցրեք ընթացիկ աշխատաշրջանը"</string> + <string name="controls_media_active_session" msgid="1984383994625845642">"Ընթացիկ աշխատաշրջանը չի կարող թաքցվել։"</string> <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Փակել"</string> <string name="controls_media_resume" msgid="1933520684481586053">"Շարունակել"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"Կարգավորումներ"</string> diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml index 750a38f58220..f21952098a57 100644 --- a/packages/SystemUI/res/values-in/strings.xml +++ b/packages/SystemUI/res/values-in/strings.xml @@ -86,6 +86,8 @@ <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Coba ambil screenshot lagi"</string> <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Tidak dapat menyimpan screenshot karena ruang penyimpanan terbatas"</string> <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Mengambil screenshot tidak diizinkan oleh aplikasi atau organisasi"</string> + <!-- no translation found for screenshot_edit (3510496440489019191) --> + <skip /> <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Menutup screenshot"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"Pratinjau screenshot"</string> <string name="screenrecord_name" msgid="2596401223859996572">"Perekam Layar"</string> @@ -1063,6 +1065,8 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Memuat rekomendasi"</string> <string name="controls_media_title" msgid="1746947284862928133">"Media"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"Menyembunyikan sesi saat ini."</string> + <!-- no translation found for controls_media_active_session (1984383994625845642) --> + <skip /> <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Tutup"</string> <string name="controls_media_resume" msgid="1933520684481586053">"Lanjutkan"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"Setelan"</string> diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml index 944e13faf3cb..8f1049f7d5a9 100644 --- a/packages/SystemUI/res/values-is/strings.xml +++ b/packages/SystemUI/res/values-is/strings.xml @@ -86,6 +86,8 @@ <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Prófaðu að taka skjámynd aftur"</string> <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Ekki tókst að vista skjámynd vegna takmarkaðs geymslupláss"</string> <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Forritið eða fyrirtækið þitt leyfir ekki skjámyndatöku"</string> + <!-- no translation found for screenshot_edit (3510496440489019191) --> + <skip /> <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Loka skjámynd"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"Forskoðun skjámyndar"</string> <string name="screenrecord_name" msgid="2596401223859996572">"Skjáupptaka"</string> @@ -1063,6 +1065,8 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Hleður tillögum"</string> <string name="controls_media_title" msgid="1746947284862928133">"Margmiðlunarefni"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"Fela núverandi lotu."</string> + <!-- no translation found for controls_media_active_session (1984383994625845642) --> + <skip /> <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Hunsa"</string> <string name="controls_media_resume" msgid="1933520684481586053">"Halda áfram"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"Stillingar"</string> diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml index 68c64f0b6955..a273ea8ad7ac 100644 --- a/packages/SystemUI/res/values-it/strings.xml +++ b/packages/SystemUI/res/values-it/strings.xml @@ -86,6 +86,8 @@ <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Riprova ad acquisire lo screenshot"</string> <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Impossibile salvare lo screenshot a causa dello spazio di archiviazione limitato"</string> <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"L\'acquisizione di screenshot non è consentita dall\'app o dall\'organizzazione"</string> + <!-- no translation found for screenshot_edit (3510496440489019191) --> + <skip /> <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Ignora screenshot"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"Anteprima screenshot"</string> <string name="screenrecord_name" msgid="2596401223859996572">"Registrazione dello schermo"</string> @@ -1063,6 +1065,8 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Caricamento dei consigli"</string> <string name="controls_media_title" msgid="1746947284862928133">"Contenuti multimediali"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"Nascondi la sessione attuale."</string> + <!-- no translation found for controls_media_active_session (1984383994625845642) --> + <skip /> <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Ignora"</string> <string name="controls_media_resume" msgid="1933520684481586053">"Riprendi"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"Impostazioni"</string> diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml index e1ad35db002a..5ae10ad72a45 100644 --- a/packages/SystemUI/res/values-iw/strings.xml +++ b/packages/SystemUI/res/values-iw/strings.xml @@ -86,6 +86,8 @@ <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"יש לנסות שוב לבצע צילום מסך"</string> <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"לא היה מספיק מקום לשמור את צילום המסך"</string> <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"האפליקציה או הארגון שלך אינם מתירים ליצור צילומי מסך"</string> + <!-- no translation found for screenshot_edit (3510496440489019191) --> + <skip /> <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"סגירת צילום מסך"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"תצוגה מקדימה של צילום מסך"</string> <string name="screenrecord_name" msgid="2596401223859996572">"מקליט המסך"</string> @@ -1075,6 +1077,8 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"בטעינת המלצות"</string> <string name="controls_media_title" msgid="1746947284862928133">"מדיה"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"הסתרת הסשן הנוכחי."</string> + <!-- no translation found for controls_media_active_session (1984383994625845642) --> + <skip /> <string name="controls_media_dismiss_button" msgid="9081375542265132213">"סגירה"</string> <string name="controls_media_resume" msgid="1933520684481586053">"המשך"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"הגדרות"</string> diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml index 763a80e48547..5d22c9a0c295 100644 --- a/packages/SystemUI/res/values-ja/strings.xml +++ b/packages/SystemUI/res/values-ja/strings.xml @@ -86,6 +86,8 @@ <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"スクリーンショットを撮り直してください"</string> <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"空き容量が足りないため、スクリーンショットを保存できません"</string> <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"スクリーンショットの作成はアプリまたは組織で許可されていません"</string> + <!-- no translation found for screenshot_edit (3510496440489019191) --> + <skip /> <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"スクリーンショットを閉じます"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"スクリーンショットのプレビュー"</string> <string name="screenrecord_name" msgid="2596401223859996572">"スクリーン レコーダー"</string> @@ -1063,6 +1065,8 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"候補を読み込んでいます"</string> <string name="controls_media_title" msgid="1746947284862928133">"メディア"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"現在のセッションを非表示にします。"</string> + <!-- no translation found for controls_media_active_session (1984383994625845642) --> + <skip /> <string name="controls_media_dismiss_button" msgid="9081375542265132213">"閉じる"</string> <string name="controls_media_resume" msgid="1933520684481586053">"再開"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"設定"</string> diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml index 0086868ace6b..97aea295c68d 100644 --- a/packages/SystemUI/res/values-ka/strings.xml +++ b/packages/SystemUI/res/values-ka/strings.xml @@ -86,6 +86,7 @@ <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"ხელახლა ცადეთ ეკრანის ანაბეჭდის გაკეთება"</string> <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"ეკრანის ანაბეჭდის შენახვა ვერ მოხერხდა შეზღუდული მეხსიერების გამო"</string> <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"ეკრანის ანაბეჭდების შექმნა არ არის ნებადართული აპის ან თქვენი ორგანიზაციის მიერ"</string> + <string name="screenshot_edit" msgid="3510496440489019191">"ეკრანის ანაბეჭდის რედაქტირება"</string> <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"ეკრანის ანაბეჭდის დახურვა"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"ეკრანის ანაბეჭდის გადახედვა"</string> <string name="screenrecord_name" msgid="2596401223859996572">"ეკრანის ჩამწერი"</string> @@ -1063,6 +1064,7 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"მიმდინარეობს რეკომენდაციების ჩატვირთვა"</string> <string name="controls_media_title" msgid="1746947284862928133">"მედია"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"დაიმალოს მიმდინარე სესია"</string> + <string name="controls_media_active_session" msgid="1984383994625845642">"მიმდინარე სესიის დამალვა შეუძლებელია."</string> <string name="controls_media_dismiss_button" msgid="9081375542265132213">"დახურვა"</string> <string name="controls_media_resume" msgid="1933520684481586053">"გაგრძელება"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"პარამეტრები"</string> diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml index f1c012169998..68cd2474c5aa 100644 --- a/packages/SystemUI/res/values-kk/strings.xml +++ b/packages/SystemUI/res/values-kk/strings.xml @@ -86,6 +86,8 @@ <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Қайта скриншот жасап көріңіз"</string> <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Жадтағы шектеулі бос орынға байланысты скриншот сақталмайды"</string> <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Қолданба немесе ұйым скриншоттар түсіруге рұқсат етпейді"</string> + <!-- no translation found for screenshot_edit (3510496440489019191) --> + <skip /> <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Скриншотты жабу"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"Скриншотты алдын ала қарау"</string> <string name="screenrecord_name" msgid="2596401223859996572">"Экран жазғыш"</string> @@ -1063,6 +1065,8 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Жүктеуге қатысты ұсыныстар"</string> <string name="controls_media_title" msgid="1746947284862928133">"Мультимедиа"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"Ағымдағы сеансты жасыру"</string> + <!-- no translation found for controls_media_active_session (1984383994625845642) --> + <skip /> <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Жабу"</string> <string name="controls_media_resume" msgid="1933520684481586053">"Жалғастыру"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"Параметрлер"</string> diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml index 92d7cbfe54b1..55f3f44f7b1f 100644 --- a/packages/SystemUI/res/values-km/strings.xml +++ b/packages/SystemUI/res/values-km/strings.xml @@ -86,6 +86,8 @@ <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"សាកល្បងថតរូបថតអេក្រង់ម្តងទៀត"</string> <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"មិនអាចរក្សាទុករូបថតអេក្រង់បានទេ ដោយសារទំហំផ្ទុកមានកម្រិតទាប"</string> <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"ការថតរូបអេក្រង់មិនត្រូវបានអនុញ្ញាតដោយកម្មវិធីនេះ ឬស្ថាប័នរបស់អ្នក"</string> + <!-- no translation found for screenshot_edit (3510496440489019191) --> + <skip /> <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"ច្រានចោលរូបថតអេក្រង់"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"ការមើលរូបថតអេក្រង់សាកល្បង"</string> <string name="screenrecord_name" msgid="2596401223859996572">"មុខងារថតអេក្រង់"</string> @@ -1063,6 +1065,8 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"កំពុងផ្ទុកការណែនាំ"</string> <string name="controls_media_title" msgid="1746947284862928133">"មេឌៀ"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"លាក់វគ្គបច្ចុប្បន្ន។"</string> + <!-- no translation found for controls_media_active_session (1984383994625845642) --> + <skip /> <string name="controls_media_dismiss_button" msgid="9081375542265132213">"ច្រានចោល"</string> <string name="controls_media_resume" msgid="1933520684481586053">"បន្ត"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"ការកំណត់"</string> diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml index 2e47e9e7c55b..569837bc6612 100644 --- a/packages/SystemUI/res/values-kn/strings.xml +++ b/packages/SystemUI/res/values-kn/strings.xml @@ -86,6 +86,8 @@ <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"ಸ್ಕ್ರೀನ್ಶಾಟ್ ಅನ್ನು ಪುನಃ ತೆಗೆದುಕೊಳ್ಳಲು ಪ್ರಯತ್ನಿಸಿ"</string> <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"ಪರಿಮಿತ ಸಂಗ್ರಹಣೆ ಸ್ಥಳದ ಕಾರಣದಿಂದಾಗಿ ಸ್ಕ್ರೀನ್ಶಾಟ್ ಉಳಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ"</string> <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"ಅಪ್ಲಿಕೇಶನ್ ಅಥವಾ ಸಂಸ್ಥೆಯು ಸ್ಕ್ರೀನ್ಶಾಟ್ಗಳನ್ನು ತೆಗೆಯುವುದನ್ನು ಅನುಮತಿಸುವುದಿಲ್ಲ"</string> + <!-- no translation found for screenshot_edit (3510496440489019191) --> + <skip /> <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"ಸ್ಕ್ರೀನ್ಶಾಟ್ ಅನ್ನು ವಜಾಗೊಳಿಸಿ"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"ಸ್ಕ್ರೀನ್ಶಾಟ್ನ ಪೂರ್ವವೀಕ್ಷಣೆ"</string> <string name="screenrecord_name" msgid="2596401223859996572">"ಸ್ಕ್ರೀನ್ ರೆಕಾರ್ಡರ್"</string> @@ -1063,6 +1065,8 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"ಶಿಫಾರಸುಗಳು ಲೋಡ್ ಆಗುತ್ತಿವೆ"</string> <string name="controls_media_title" msgid="1746947284862928133">"ಮಾಧ್ಯಮ"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"ಪ್ರಸ್ತುತ ಸೆಶನ್ ಅನ್ನು ಮರೆಮಾಡಿ."</string> + <!-- no translation found for controls_media_active_session (1984383994625845642) --> + <skip /> <string name="controls_media_dismiss_button" msgid="9081375542265132213">"ವಜಾಗೊಳಿಸಿ"</string> <string name="controls_media_resume" msgid="1933520684481586053">"ಪುನರಾರಂಭಿಸಿ"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"ಸೆಟ್ಟಿಂಗ್ಗಳು"</string> diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml index fc533be79c2c..1fdff749151f 100644 --- a/packages/SystemUI/res/values-ko/strings.xml +++ b/packages/SystemUI/res/values-ko/strings.xml @@ -86,6 +86,8 @@ <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"스크린샷을 다시 찍어 보세요."</string> <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"저장용량이 부족하여 스크린샷을 저장할 수 없습니다"</string> <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"앱이나 조직에서 스크린샷 촬영을 허용하지 않습니다."</string> + <!-- no translation found for screenshot_edit (3510496440489019191) --> + <skip /> <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"스크린샷 닫기"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"스크린샷 미리보기"</string> <string name="screenrecord_name" msgid="2596401223859996572">"화면 녹화"</string> @@ -1063,6 +1065,8 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"추천 제어 기능 로드 중"</string> <string name="controls_media_title" msgid="1746947284862928133">"미디어"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"현재 세션을 숨깁니다."</string> + <!-- no translation found for controls_media_active_session (1984383994625845642) --> + <skip /> <string name="controls_media_dismiss_button" msgid="9081375542265132213">"닫기"</string> <string name="controls_media_resume" msgid="1933520684481586053">"다시 시작"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"설정"</string> diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml index a4e47b39d454..970d5fab09cb 100644 --- a/packages/SystemUI/res/values-ky/strings.xml +++ b/packages/SystemUI/res/values-ky/strings.xml @@ -86,6 +86,8 @@ <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Скриншотту кайра тартып көрүңүз"</string> <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Сактагычта бош орун аз болгондуктан, скриншот сакталбай жатат"</string> <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Скриншот тартууга колдонмо же ишканаңыз тыюу салган."</string> + <!-- no translation found for screenshot_edit (3510496440489019191) --> + <skip /> <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Скриншотту четке кагуу"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"Скриншотту алдын ала көрүү"</string> <string name="screenrecord_name" msgid="2596401223859996572">"экрандан видео жаздырып алуу"</string> @@ -1063,6 +1065,8 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Сунуштар жүктөлүүдө"</string> <string name="controls_media_title" msgid="1746947284862928133">"Медиа"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"Учурдагы сеансты жашыруу."</string> + <!-- no translation found for controls_media_active_session (1984383994625845642) --> + <skip /> <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Жабуу"</string> <string name="controls_media_resume" msgid="1933520684481586053">"Улантуу"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"Жөндөөлөр"</string> diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml index 78fdf0964707..9f0f0ae0eb59 100644 --- a/packages/SystemUI/res/values-lo/strings.xml +++ b/packages/SystemUI/res/values-lo/strings.xml @@ -86,6 +86,7 @@ <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"ກະລຸນາລອງຖ່າຍຮູບໜ້າຈໍອີກຄັ້ງ"</string> <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"ບໍ່ສາມາດຖ່າຍຮູບໜ້າຈໍໄດ້ເນື່ອງຈາກພື້ນທີ່ຈັດເກັບຂໍ້ມູນມີຈຳກັດ"</string> <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"ແອັບ ຫຼື ອົງກອນຂອງທ່ານບໍ່ອະນຸຍາດໃຫ້ຖ່າຍຮູບໜ້າຈໍ"</string> + <string name="screenshot_edit" msgid="3510496440489019191">"ແກ້ໄຂຮູບໜ້າຈໍ"</string> <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"ປິດຮູບໜ້າຈໍ"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"ຕົວຢ່າງຮູບໜ້າຈໍ"</string> <string name="screenrecord_name" msgid="2596401223859996572">"ໂປຣແກຣມບັນທຶກໜ້າຈໍ"</string> @@ -1063,6 +1064,7 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"ກຳລັງໂຫຼດຄຳແນະນຳ"</string> <string name="controls_media_title" msgid="1746947284862928133">"ມີເດຍ"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"ເຊື່ອງເຊດຊັນປັດຈຸບັນ."</string> + <string name="controls_media_active_session" msgid="1984383994625845642">"ບໍ່ສາມາດເຊື່ອເຊດຊັນປັດຈຸບັນໄດ້."</string> <string name="controls_media_dismiss_button" msgid="9081375542265132213">"ປິດໄວ້"</string> <string name="controls_media_resume" msgid="1933520684481586053">"ສືບຕໍ່"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"ການຕັ້ງຄ່າ"</string> diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml index 6db8969e0238..81696c2c88df 100644 --- a/packages/SystemUI/res/values-lt/strings.xml +++ b/packages/SystemUI/res/values-lt/strings.xml @@ -86,6 +86,8 @@ <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Pabandykite padaryti ekrano kopiją dar kartą"</string> <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Negalima išsaugoti ekrano kopijos dėl ribotos saugyklos vietos"</string> <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Jūsų organizacijoje arba naudojant šią programą neleidžiama daryti ekrano kopijų"</string> + <!-- no translation found for screenshot_edit (3510496440489019191) --> + <skip /> <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Praleisti ekrano kopiją"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"Ekrano kopijos peržiūra"</string> <string name="screenrecord_name" msgid="2596401223859996572">"Ekrano vaizdo įrašytuvas"</string> @@ -1075,6 +1077,8 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Įkeliamos rekomendacijos"</string> <string name="controls_media_title" msgid="1746947284862928133">"Medija"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"Slėpti dabartinį seansą."</string> + <!-- no translation found for controls_media_active_session (1984383994625845642) --> + <skip /> <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Atsisakyti"</string> <string name="controls_media_resume" msgid="1933520684481586053">"Tęsti"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"Nustatymai"</string> diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml index c7807deb3a5c..d37dd914865c 100644 --- a/packages/SystemUI/res/values-lv/strings.xml +++ b/packages/SystemUI/res/values-lv/strings.xml @@ -86,6 +86,8 @@ <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Mēģiniet izveidot jaunu ekrānuzņēmumu."</string> <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Nevar saglabāt ekrānuzņēmumu, jo krātuvē nepietiek vietas."</string> <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Lietotne vai jūsu organizācija neatļauj veikt ekrānuzņēmumus."</string> + <!-- no translation found for screenshot_edit (3510496440489019191) --> + <skip /> <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Nerādīt ekrānuzņēmumu"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"Ekrānuzņēmuma priekšskatījums"</string> <string name="screenrecord_name" msgid="2596401223859996572">"Ekrāna ierakstītājs"</string> @@ -1069,6 +1071,8 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Notiek ieteikumu ielāde"</string> <string name="controls_media_title" msgid="1746947284862928133">"Multivide"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"Paslēpiet pašreizējo sesiju."</string> + <!-- no translation found for controls_media_active_session (1984383994625845642) --> + <skip /> <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Nerādīt"</string> <string name="controls_media_resume" msgid="1933520684481586053">"Atsākt"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"Iestatījumi"</string> diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml index 203f6c24609f..28c4ee7fc951 100644 --- a/packages/SystemUI/res/values-mk/strings.xml +++ b/packages/SystemUI/res/values-mk/strings.xml @@ -86,6 +86,8 @@ <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Повторно обидете се да направите слика од екранот"</string> <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Сликата од екранот не може да се зачува поради ограничена меморија"</string> <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Апликацијата или вашата организација не дозволува снимање слики од екранот"</string> + <!-- no translation found for screenshot_edit (3510496440489019191) --> + <skip /> <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Отфрлете ја сликата од екранот"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"Преглед на слика од екранот"</string> <string name="screenrecord_name" msgid="2596401223859996572">"Снимач на екран"</string> @@ -1063,6 +1065,8 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Се вчитуваат препораки"</string> <string name="controls_media_title" msgid="1746947284862928133">"Аудиовизуелни содржини"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"Сокриј ја тековнава сесија."</string> + <!-- no translation found for controls_media_active_session (1984383994625845642) --> + <skip /> <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Отфрли"</string> <string name="controls_media_resume" msgid="1933520684481586053">"Продолжи"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"Поставки"</string> diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml index 1076eace0223..f94264103549 100644 --- a/packages/SystemUI/res/values-ml/strings.xml +++ b/packages/SystemUI/res/values-ml/strings.xml @@ -86,6 +86,8 @@ <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"സ്ക്രീൻഷോട്ട് എടുക്കാൻ വീണ്ടും ശ്രമിക്കുക"</string> <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"സ്റ്റോറേജ് ഇടം പരിമിതമായതിനാൽ സ്ക്രീൻഷോട്ട് സംരക്ഷിക്കാനാകുന്നില്ല"</string> <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"സ്ക്രീൻഷോട്ടുകൾ എടുക്കുന്നത് ആപ്പോ നിങ്ങളുടെ സ്ഥാപനമോ അനുവദിക്കുന്നില്ല"</string> + <!-- no translation found for screenshot_edit (3510496440489019191) --> + <skip /> <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"സ്ക്രീൻഷോട്ട് ഡിസ്മിസ് ചെയ്യുക"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"സ്ക്രീൻഷോട്ട് പ്രിവ്യു"</string> <string name="screenrecord_name" msgid="2596401223859996572">"സ്ക്രീൻ റെക്കോർഡർ"</string> @@ -1063,6 +1065,8 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"നിർദ്ദേശങ്ങൾ ലോഡ് ചെയ്യുന്നു"</string> <string name="controls_media_title" msgid="1746947284862928133">"മീഡിയ"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"നിലവിലെ സെഷൻ മറയ്ക്കുക."</string> + <!-- no translation found for controls_media_active_session (1984383994625845642) --> + <skip /> <string name="controls_media_dismiss_button" msgid="9081375542265132213">"ഡിസ്മിസ് ചെയ്യുക"</string> <string name="controls_media_resume" msgid="1933520684481586053">"പുനരാരംഭിക്കുക"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"ക്രമീകരണം"</string> diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml index c1ec99ee1da5..fc41f6cc0739 100644 --- a/packages/SystemUI/res/values-mn/strings.xml +++ b/packages/SystemUI/res/values-mn/strings.xml @@ -86,6 +86,8 @@ <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Дэлгэцийн зургийг дахин дарж үзнэ үү"</string> <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Сангийн багтаамж бага байгаа тул дэлгэцээс дарсан зургийг хадгалах боломжгүй байна"</string> <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Таны апп, байгууллагад дэлгэцийн зураг авахыг зөвшөөрдөггүй"</string> + <!-- no translation found for screenshot_edit (3510496440489019191) --> + <skip /> <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Дэлгэцийн агшныг хаах"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"Дэлгэцийн агшныг урьдчилан үзэх"</string> <string name="screenrecord_name" msgid="2596401223859996572">"Дэлгэцийн үйлдэл бичигч"</string> @@ -1063,6 +1065,8 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Зөвлөмжүүдийг ачаалж байна"</string> <string name="controls_media_title" msgid="1746947284862928133">"Медиа"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"Одоогийн харилцан үйлдлийг нуугаарай."</string> + <!-- no translation found for controls_media_active_session (1984383994625845642) --> + <skip /> <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Хаах"</string> <string name="controls_media_resume" msgid="1933520684481586053">"Үргэлжлүүлэх"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"Тохиргоо"</string> diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml index 40a51c77d4df..2b01fef142bd 100644 --- a/packages/SystemUI/res/values-mr/strings.xml +++ b/packages/SystemUI/res/values-mr/strings.xml @@ -86,6 +86,8 @@ <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"स्क्रीनशॉट पुन्हा घेण्याचा प्रयत्न करा"</string> <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"मर्यादित स्टोरेज जागेमुळे स्क्रीनशॉट सेव्ह करू शकत नाही"</string> <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"अॅप किंवा आपल्या संस्थेद्वारे स्क्रीनशॉट घेण्याची अनुमती नाही"</string> + <!-- no translation found for screenshot_edit (3510496440489019191) --> + <skip /> <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"स्क्रीनशॉट डिसमिस करा"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"स्क्रीनशॉटचे पूर्वावलोकन"</string> <string name="screenrecord_name" msgid="2596401223859996572">"स्क्रीन रेकॉर्डर"</string> @@ -265,7 +267,7 @@ <string name="accessibility_quick_settings_wifi" msgid="167707325133803052">"<xliff:g id="SIGNAL">%1$s</xliff:g>."</string> <string name="accessibility_quick_settings_wifi_changed_off" msgid="2230487165558877262">"Wifi बंद झाले."</string> <string name="accessibility_quick_settings_wifi_changed_on" msgid="1490362586009027611">"Wifi सुरू झाले."</string> - <string name="accessibility_quick_settings_mobile" msgid="1817825313718492906">"मोबाईल <xliff:g id="SIGNAL">%1$s</xliff:g>. <xliff:g id="TYPE">%2$s</xliff:g>. <xliff:g id="NETWORK">%3$s</xliff:g>."</string> + <string name="accessibility_quick_settings_mobile" msgid="1817825313718492906">"मोबाइल <xliff:g id="SIGNAL">%1$s</xliff:g>. <xliff:g id="TYPE">%2$s</xliff:g>. <xliff:g id="NETWORK">%3$s</xliff:g>."</string> <string name="accessibility_quick_settings_battery" msgid="533594896310663853">"बॅटरी <xliff:g id="STATE">%s</xliff:g>."</string> <string name="accessibility_quick_settings_airplane_off" msgid="1275658769368793228">"विमान मोड बंद."</string> <string name="accessibility_quick_settings_airplane_on" msgid="8106176561295294255">"विमान मोड सुरू."</string> @@ -298,8 +300,8 @@ <string name="accessibility_quick_settings_flashlight_changed_on" msgid="4747870681508334200">"फ्लॅशलाइट सुरू केला."</string> <string name="accessibility_quick_settings_color_inversion_changed_off" msgid="7548045840282925393">"रंग उत्क्रमण बंद केले."</string> <string name="accessibility_quick_settings_color_inversion_changed_on" msgid="4711141858364404084">"रंग उत्क्रमण सुरू केले."</string> - <string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"मोबाईल हॉटस्पॉट बंद केला."</string> - <string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"मोबाईल हॉटस्पॉट सुरू केला."</string> + <string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"मोबाइल हॉटस्पॉट बंद केला."</string> + <string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"मोबाइल हॉटस्पॉट सुरू केला."</string> <string name="accessibility_casting_turned_off" msgid="1387906158563374962">"स्क्रीन कास्ट करणे थांबले."</string> <string name="accessibility_quick_settings_work_mode_off" msgid="562749867895549696">"कार्य मोड बंद."</string> <string name="accessibility_quick_settings_work_mode_on" msgid="2779253456042059110">"कार्य मोड सुरू."</string> @@ -723,7 +725,7 @@ <string name="bubble_overflow_empty_title" msgid="3120029421991510842">"अलीकडील कोणतेही बबल नाहीत"</string> <string name="bubble_overflow_empty_subtitle" msgid="2030874469510497397">"अलीकडील बबल आणि डिसमिस केलेले बबल येथे दिसतील"</string> <string name="notification_unblockable_desc" msgid="2073030886006190804">"या सूचनांमध्ये सुधारणा केली जाऊ शकत नाही."</string> - <string name="notification_multichannel_desc" msgid="7414593090056236179">"या सूचनांचा संच येथे कॉन्फिगर केला जाऊ शकत नाही"</string> + <string name="notification_multichannel_desc" msgid="7414593090056236179">"या सूचनांचा संच येथे कॉंफिगर केला जाऊ शकत नाही"</string> <string name="notification_delegate_header" msgid="1264510071031479920">"प्रॉक्सी केलेल्या सूचना"</string> <string name="notification_channel_dialog_title" msgid="6856514143093200019">"सर्व <xliff:g id="APP_NAME">%1$s</xliff:g> वरील सूचना"</string> <string name="see_more_title" msgid="7409317011708185729">"आणखी पाहा"</string> @@ -1018,14 +1020,10 @@ <string name="accessibility_control_move_down" msgid="5390922476900974512">"खाली हलवा"</string> <string name="accessibility_control_move_left" msgid="8156206978511401995">"डावीकडे हलवा"</string> <string name="accessibility_control_move_right" msgid="8926821093629582888">"उजवीकडे हलवा"</string> - <!-- no translation found for magnification_mode_switch_description (2698364322069934733) --> - <skip /> - <!-- no translation found for magnification_mode_switch_state_full_screen (2882507327576770574) --> - <skip /> - <!-- no translation found for magnification_mode_switch_state_window (8597100249594076965) --> - <skip /> - <!-- no translation found for magnification_mode_switch_click_label (2786203505805898199) --> - <skip /> + <string name="magnification_mode_switch_description" msgid="2698364322069934733">"मॅग्निफिकेशन स्विच"</string> + <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"संपूर्ण स्क्रीन मॅग्निफाय करा"</string> + <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"स्क्रीनचा काही भाग मॅग्निफाय करा"</string> + <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"स्विच करा"</string> <string name="quick_controls_title" msgid="6839108006171302273">"डिव्हाइस नियंत्रणे"</string> <string name="quick_controls_subtitle" msgid="1667408093326318053">"तुमच्या कनेक्ट केलेल्या डिव्हाइससाठी नियंत्रणे जोडा"</string> <string name="quick_controls_setup_title" msgid="8901436655997849822">"डिव्हाइस नियंत्रणे सेट करा"</string> @@ -1067,6 +1065,8 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"शिफारशी लोड करत आहे"</string> <string name="controls_media_title" msgid="1746947284862928133">"मीडिया"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"सध्याचे सेशन लपवा."</string> + <!-- no translation found for controls_media_active_session (1984383994625845642) --> + <skip /> <string name="controls_media_dismiss_button" msgid="9081375542265132213">"डिसमिस करा"</string> <string name="controls_media_resume" msgid="1933520684481586053">"पुन्हा सुरू करा"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"सेटिंग्ज"</string> diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml index ac22f4c37b56..5b0f91f5f967 100644 --- a/packages/SystemUI/res/values-ms/strings.xml +++ b/packages/SystemUI/res/values-ms/strings.xml @@ -86,6 +86,8 @@ <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Cuba ambil tangkapan skrin sekali lagi"</string> <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Tidak dapat menyimpan tangkapan skrin kerana ruang storan terhad"</string> <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Pengambilan tangkapan skrin tidak dibenarkan oleh apl atau organisasi anda"</string> + <!-- no translation found for screenshot_edit (3510496440489019191) --> + <skip /> <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Ketepikan tangkapan skrin"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"Pratonton tangkapan skrin"</string> <string name="screenrecord_name" msgid="2596401223859996572">"Perakam Skrin"</string> @@ -1063,6 +1065,8 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Memuatkan cadangan"</string> <string name="controls_media_title" msgid="1746947284862928133">"Media"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"Sembunyikan sesi semasa."</string> + <!-- no translation found for controls_media_active_session (1984383994625845642) --> + <skip /> <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Tolak"</string> <string name="controls_media_resume" msgid="1933520684481586053">"Sambung semula"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"Tetapan"</string> diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml index c92900cd825d..b814607226e3 100644 --- a/packages/SystemUI/res/values-my/strings.xml +++ b/packages/SystemUI/res/values-my/strings.xml @@ -86,6 +86,8 @@ <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"မျက်နှာပြင်ပုံကို ထပ်ရိုက်ကြည့်ပါ"</string> <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"သိုလှောင်ခန်းနေရာ အကန့်အသတ်ရှိသောကြောင့် ဖန်သားပြင်ဓာတ်ပုံကို သိမ်းဆည်း၍မရပါ"</string> <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"ဖန်သားပြင်ဓာတ်ပုံရိုက်ကူးခြင်းကို ဤအက်ပ် သို့မဟုတ် သင်၏အဖွဲ့အစည်းက ခွင့်မပြုပါ"</string> + <!-- no translation found for screenshot_edit (3510496440489019191) --> + <skip /> <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"ဖန်သားပြင်ဓာတ်ပုံ ပယ်ရန်"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"ဖန်သားပြင်ဓာတ်ပုံ အစမ်းကြည့်ရှုခြင်း"</string> <string name="screenrecord_name" msgid="2596401223859996572">"ဖန်သားပြင် ရိုက်ကူးမှု"</string> @@ -1063,6 +1065,8 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"အကြံပြုချက်များ ဖွင့်နေသည်"</string> <string name="controls_media_title" msgid="1746947284862928133">"မီဒီယာ"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"လက်ရှိ စက်ရှင်ကို ဖျောက်ထားမည်။"</string> + <!-- no translation found for controls_media_active_session (1984383994625845642) --> + <skip /> <string name="controls_media_dismiss_button" msgid="9081375542265132213">"ပယ်ရန်"</string> <string name="controls_media_resume" msgid="1933520684481586053">"ဆက်လုပ်ရန်"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"ဆက်တင်များ"</string> diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml index 2920af3da628..b676e91398e1 100644 --- a/packages/SystemUI/res/values-nb/strings.xml +++ b/packages/SystemUI/res/values-nb/strings.xml @@ -86,6 +86,7 @@ <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Prøv å ta skjermdump på nytt"</string> <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Kan ikke lagre skjermdumpen på grunn av begrenset lagringsplass"</string> <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Appen eller organisasjonen din tillater ikke at du tar skjermdumper"</string> + <string name="screenshot_edit" msgid="3510496440489019191">"Rediger skjermdumpen"</string> <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Avvis skjermdumpen"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"Forhåndsvisning av skjermdump"</string> <string name="screenrecord_name" msgid="2596401223859996572">"Skjermopptaker"</string> @@ -1063,6 +1064,7 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Laster inn anbefalinger"</string> <string name="controls_media_title" msgid="1746947284862928133">"Medier"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"Skjul den nåværende økten."</string> + <string name="controls_media_active_session" msgid="1984383994625845642">"Den nåværende økten kan ikke skjules."</string> <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Lukk"</string> <string name="controls_media_resume" msgid="1933520684481586053">"Gjenoppta"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"Innstillinger"</string> diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml index 6b77bb2ebeb0..5eea7fd4abb1 100644 --- a/packages/SystemUI/res/values-ne/strings.xml +++ b/packages/SystemUI/res/values-ne/strings.xml @@ -86,6 +86,8 @@ <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"स्क्रिनसट फेरि लिएर हेर्नुहोस्"</string> <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"भण्डारण ठाउँ सीमित भएका कारण स्क्रिनसट सुरक्षित गर्न सकिएन"</string> <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"उक्त एप वा तपाईंको संगठनले स्क्रिनसटहरू लिन दिँदैन"</string> + <!-- no translation found for screenshot_edit (3510496440489019191) --> + <skip /> <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"स्क्रिनसट हटाउनुहोस्"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"स्क्रिनसटको पूर्वावलोकन"</string> <string name="screenrecord_name" msgid="2596401223859996572">"स्क्रिन रेकर्डर"</string> @@ -366,7 +368,7 @@ <string name="quick_settings_location_off_label" msgid="7923929131443915919">"स्थान बन्द छ"</string> <string name="quick_settings_media_device_label" msgid="8034019242363789941">"मिडिया उपकरण"</string> <string name="quick_settings_rssi_label" msgid="3397615415140356701">"RSSI"</string> - <string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"आपतकालीन कल मात्र"</string> + <string name="quick_settings_rssi_emergency_only" msgid="7499207215265078598">"आपत्कालीन कल मात्र"</string> <string name="quick_settings_settings_label" msgid="2214639529565474534">"सेटिङहरू"</string> <string name="quick_settings_time_label" msgid="3352680970557509303">"समय"</string> <string name="quick_settings_user_label" msgid="1253515509432672496">"मलाई"</string> @@ -1018,14 +1020,10 @@ <string name="accessibility_control_move_down" msgid="5390922476900974512">"तल सार्नुहोस्"</string> <string name="accessibility_control_move_left" msgid="8156206978511401995">"बायाँ सार्नुहोस्"</string> <string name="accessibility_control_move_right" msgid="8926821093629582888">"दायाँ सार्नुहोस्"</string> - <!-- no translation found for magnification_mode_switch_description (2698364322069934733) --> - <skip /> - <!-- no translation found for magnification_mode_switch_state_full_screen (2882507327576770574) --> - <skip /> - <!-- no translation found for magnification_mode_switch_state_window (8597100249594076965) --> - <skip /> - <!-- no translation found for magnification_mode_switch_click_label (2786203505805898199) --> - <skip /> + <string name="magnification_mode_switch_description" msgid="2698364322069934733">"म्याग्निफिकेसन स्विच"</string> + <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"पूरै स्क्रिन म्याग्निफाइ गर्नुहोस्"</string> + <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"स्क्रिनको केही भाग म्याग्निफाइ गर्नुहोस्"</string> + <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"बदल्नुहोस्"</string> <string name="quick_controls_title" msgid="6839108006171302273">"यन्त्र नियन्त्रण गर्ने विजेटहरू"</string> <string name="quick_controls_subtitle" msgid="1667408093326318053">"आफ्ना जोडिएका यन्त्रहरूका लागि नियन्त्रण सुविधाहरू थप्नुहोस्"</string> <string name="quick_controls_setup_title" msgid="8901436655997849822">"यन्त्र नियन्त्रण गर्ने विजेटहरू सेटअप गर्नुहोस्"</string> @@ -1067,6 +1065,8 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"सिफारिसहरू लोड गर्दै"</string> <string name="controls_media_title" msgid="1746947284862928133">"मिडिया"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"हालको सत्र लुकाउनुहोस्।"</string> + <!-- no translation found for controls_media_active_session (1984383994625845642) --> + <skip /> <string name="controls_media_dismiss_button" msgid="9081375542265132213">"हटाउनुहोस्"</string> <string name="controls_media_resume" msgid="1933520684481586053">"सुचारु गर्नुहोस्"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"सेटिङ"</string> diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml index b90cd3000dc7..2126b1a451f7 100644 --- a/packages/SystemUI/res/values-nl/strings.xml +++ b/packages/SystemUI/res/values-nl/strings.xml @@ -86,6 +86,8 @@ <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Probeer opnieuw een screenshot te maken"</string> <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Kan screenshot niet opslaan vanwege beperkte opslagruimte"</string> <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Het maken van screenshots wordt niet toegestaan door de app of je organisatie"</string> + <!-- no translation found for screenshot_edit (3510496440489019191) --> + <skip /> <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Screenshot sluiten"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"Voorbeeld van screenshot"</string> <string name="screenrecord_name" msgid="2596401223859996572">"Schermopname"</string> @@ -1063,6 +1065,8 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Aanbevelingen laden"</string> <string name="controls_media_title" msgid="1746947284862928133">"Media"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"De huidige sessie verbergen."</string> + <!-- no translation found for controls_media_active_session (1984383994625845642) --> + <skip /> <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Sluiten"</string> <string name="controls_media_resume" msgid="1933520684481586053">"Hervatten"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"Instellingen"</string> diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml index fd7692aaac68..b55fd67ba6a1 100644 --- a/packages/SystemUI/res/values-or/strings.xml +++ b/packages/SystemUI/res/values-or/strings.xml @@ -86,6 +86,8 @@ <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"ପୁଣିଥରେ ସ୍କ୍ରୀନ୍ଶଟ୍ ନେବାକୁ ଚେଷ୍ଟା କରନ୍ତୁ"</string> <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"ସୀମିତ ଷ୍ଟୋରେଜ୍ ସ୍ପେସ୍ ହେତୁ ସ୍କ୍ରୀନଶଟ୍ ସେଭ୍ ହୋଇପାରିବ ନାହିଁ"</string> <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"ଆପ୍ କିମ୍ବା ସଂସ୍ଥା ଦ୍ୱାରା ସ୍କ୍ରୀନଶଟ୍ ନେବାକୁ ଅନୁମତି ଦିଆଯାଇ ନାହିଁ"</string> + <!-- no translation found for screenshot_edit (3510496440489019191) --> + <skip /> <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"ସ୍କ୍ରିନସଟ୍ ଖାରଜ କରନ୍ତୁ"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"ସ୍କ୍ରିନସଟର ପ୍ରିଭ୍ୟୁ"</string> <string name="screenrecord_name" msgid="2596401223859996572">"ସ୍କ୍ରିନ୍ ରେକର୍ଡର୍"</string> @@ -1063,6 +1065,8 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"ସୁପାରିଶଗୁଡ଼ିକ ଲୋଡ୍ କରାଯାଉଛି"</string> <string name="controls_media_title" msgid="1746947284862928133">"ମିଡିଆ"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"ବର୍ତ୍ତମାନର ସେସନ୍ ଲୁଚାନ୍ତୁ।"</string> + <!-- no translation found for controls_media_active_session (1984383994625845642) --> + <skip /> <string name="controls_media_dismiss_button" msgid="9081375542265132213">"ଖାରଜ କରନ୍ତୁ"</string> <string name="controls_media_resume" msgid="1933520684481586053">"ପୁଣି ଆରମ୍ଭ କରନ୍ତୁ"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"ସେଟିଂସ୍"</string> diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml index 19715c56c793..9714f34ab0cc 100644 --- a/packages/SystemUI/res/values-pa/strings.xml +++ b/packages/SystemUI/res/values-pa/strings.xml @@ -86,6 +86,8 @@ <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"ਸਕ੍ਰੀਨਸ਼ਾਟ ਦੁਬਾਰਾ ਲੈ ਕੇ ਦੇਖੋ"</string> <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"ਸੀਮਿਤ ਸਟੋਰੇਜ ਹੋਣ ਕਾਰਨ ਸਕ੍ਰੀਨਸ਼ਾਟ ਰੱਖਿਅਤ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਦਾ"</string> <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"ਐਪ ਜਾਂ ਤੁਹਾਡੀ ਸੰਸਥਾ ਵੱਲੋਂ ਸਕ੍ਰੀਨਸ਼ਾਟ ਲੈਣ ਦੀ ਇਜਾਜ਼ਤ ਨਹੀਂ ਦਿੱਤੀ ਗਈ ਹੈ"</string> + <!-- no translation found for screenshot_edit (3510496440489019191) --> + <skip /> <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"ਸਕ੍ਰੀਨਸ਼ਾਟ ਖਾਰਜ ਕਰੋ"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"ਸਕ੍ਰੀਨਸ਼ਾਟ ਪੂਰਵ-ਝਲਕ"</string> <string name="screenrecord_name" msgid="2596401223859996572">"ਸਕ੍ਰੀਨ ਰਿਕਾਰਡਰ"</string> @@ -1018,14 +1020,10 @@ <string name="accessibility_control_move_down" msgid="5390922476900974512">"ਹੇਠਾਂ ਲਿਜਾਓ"</string> <string name="accessibility_control_move_left" msgid="8156206978511401995">"ਖੱਬੇ ਲਿਜਾਓ"</string> <string name="accessibility_control_move_right" msgid="8926821093629582888">"ਸੱਜੇ ਲਿਜਾਓ"</string> - <!-- no translation found for magnification_mode_switch_description (2698364322069934733) --> - <skip /> - <!-- no translation found for magnification_mode_switch_state_full_screen (2882507327576770574) --> - <skip /> - <!-- no translation found for magnification_mode_switch_state_window (8597100249594076965) --> - <skip /> - <!-- no translation found for magnification_mode_switch_click_label (2786203505805898199) --> - <skip /> + <string name="magnification_mode_switch_description" msgid="2698364322069934733">"ਵੱਡਦਰਸ਼ੀਕਰਨ ਸਵਿੱਚ"</string> + <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"ਸਾਰੀ ਸਕ੍ਰੀਨ ਨੂੰ ਵੱਡਾ ਕਰੋ"</string> + <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"ਸਕ੍ਰੀਨ ਦੇ ਹਿੱਸੇ ਨੂੰ ਵੱਡਾ ਕਰੋ"</string> + <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"ਸਵਿੱਚ"</string> <string name="quick_controls_title" msgid="6839108006171302273">"ਡੀਵਾਈਸ ਕੰਟਰੋਲ"</string> <string name="quick_controls_subtitle" msgid="1667408093326318053">"ਆਪਣੇ ਕਨੈਕਟ ਕੀਤੇ ਡੀਵਾਈਸਾਂ ਲਈ ਕੰਟਰੋਲ ਸ਼ਾਮਲ ਕਰੋ"</string> <string name="quick_controls_setup_title" msgid="8901436655997849822">"ਡੀਵਾਈਸ ਕੰਟਰੋਲਾਂ ਦਾ ਸੈੱਟਅੱਪ ਕਰੋ"</string> @@ -1067,6 +1065,8 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"ਸਿਫ਼ਾਰਸ਼ਾਂ ਲੋਡ ਹੋ ਰਹੀਆਂ ਹਨ"</string> <string name="controls_media_title" msgid="1746947284862928133">"ਮੀਡੀਆ"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"ਮੌਜੂਦਾ ਸੈਸ਼ਨ ਨੂੰ ਲੁਕਾਓ।"</string> + <!-- no translation found for controls_media_active_session (1984383994625845642) --> + <skip /> <string name="controls_media_dismiss_button" msgid="9081375542265132213">"ਖਾਰਜ ਕਰੋ"</string> <string name="controls_media_resume" msgid="1933520684481586053">"ਮੁੜ-ਚਾਲੂ ਕਰੋ"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"ਸੈਟਿੰਗਾਂ"</string> diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml index 19cd0e9e7c6c..f5bcdba5cede 100644 --- a/packages/SystemUI/res/values-pl/strings.xml +++ b/packages/SystemUI/res/values-pl/strings.xml @@ -86,6 +86,8 @@ <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Spróbuj jeszcze raz wykonać zrzut ekranu"</string> <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Nie można zapisać zrzutu ekranu, bo brakuje miejsca w pamięci"</string> <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Nie możesz wykonać zrzutu ekranu, bo nie zezwala na to aplikacja lub Twoja organizacja."</string> + <!-- no translation found for screenshot_edit (3510496440489019191) --> + <skip /> <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Zamknij zrzut ekranu"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"Podgląd zrzutu ekranu"</string> <string name="screenrecord_name" msgid="2596401223859996572">"Nagrywanie ekranu"</string> @@ -1075,6 +1077,8 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Wczytuję rekomendacje"</string> <string name="controls_media_title" msgid="1746947284862928133">"Multimedia"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"Ukryj bieżącą sesję."</string> + <!-- no translation found for controls_media_active_session (1984383994625845642) --> + <skip /> <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Odrzuć"</string> <string name="controls_media_resume" msgid="1933520684481586053">"Wznów"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"Ustawienia"</string> diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml index f8cf8810c9ff..0db05d895746 100644 --- a/packages/SystemUI/res/values-pt-rBR/strings.xml +++ b/packages/SystemUI/res/values-pt-rBR/strings.xml @@ -86,6 +86,7 @@ <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Tente fazer a captura de tela novamente"</string> <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"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="7818288545874407451">"O app ou a organização não permitem capturas de tela"</string> + <string name="screenshot_edit" msgid="3510496440489019191">"Editar captura de tela"</string> <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Dispensar captura de tela"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"Visualização de captura de tela"</string> <string name="screenrecord_name" msgid="2596401223859996572">"Gravador de tela"</string> @@ -1063,6 +1064,7 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Carregando recomendações"</string> <string name="controls_media_title" msgid="1746947284862928133">"Mídia"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"Ocultar a sessão atual."</string> + <string name="controls_media_active_session" msgid="1984383994625845642">"Não é possível ocultar a sessão atual."</string> <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Dispensar"</string> <string name="controls_media_resume" msgid="1933520684481586053">"Retomar"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"Configurações"</string> diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml index 0b1c5e31d3bf..147ed1d7eebf 100644 --- a/packages/SystemUI/res/values-pt-rPT/strings.xml +++ b/packages/SystemUI/res/values-pt-rPT/strings.xml @@ -86,6 +86,7 @@ <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Experimente voltar a efetuar a captura de ecrã."</string> <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"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="7818288545874407451">"A app ou a sua entidade não permitem tirar capturas de ecrã"</string> + <string name="screenshot_edit" msgid="3510496440489019191">"Editar captura de ecrã"</string> <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Ignorar captura de ecrã"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"Pré-visualização da captura de ecrã"</string> <string name="screenrecord_name" msgid="2596401223859996572">"Gravador de ecrã"</string> @@ -1063,6 +1064,7 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"A carregar recomendações…"</string> <string name="controls_media_title" msgid="1746947284862928133">"Multimédia"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"Oculte a sessão atual."</string> + <string name="controls_media_active_session" msgid="1984383994625845642">"Não é possível ocultar a sessão atual."</string> <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Ignorar"</string> <string name="controls_media_resume" msgid="1933520684481586053">"Retomar"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"Definições"</string> diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml index f8cf8810c9ff..0db05d895746 100644 --- a/packages/SystemUI/res/values-pt/strings.xml +++ b/packages/SystemUI/res/values-pt/strings.xml @@ -86,6 +86,7 @@ <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Tente fazer a captura de tela novamente"</string> <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"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="7818288545874407451">"O app ou a organização não permitem capturas de tela"</string> + <string name="screenshot_edit" msgid="3510496440489019191">"Editar captura de tela"</string> <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Dispensar captura de tela"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"Visualização de captura de tela"</string> <string name="screenrecord_name" msgid="2596401223859996572">"Gravador de tela"</string> @@ -1063,6 +1064,7 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Carregando recomendações"</string> <string name="controls_media_title" msgid="1746947284862928133">"Mídia"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"Ocultar a sessão atual."</string> + <string name="controls_media_active_session" msgid="1984383994625845642">"Não é possível ocultar a sessão atual."</string> <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Dispensar"</string> <string name="controls_media_resume" msgid="1933520684481586053">"Retomar"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"Configurações"</string> diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml index 35021611a06c..9c73e5a1fcfd 100644 --- a/packages/SystemUI/res/values-ro/strings.xml +++ b/packages/SystemUI/res/values-ro/strings.xml @@ -86,6 +86,8 @@ <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Încercați să faceți din nou o captură de ecran"</string> <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Captura de ecran nu poate fi salvată din cauza spațiului de stocare limitat"</string> <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Crearea capturilor de ecran nu este permisă de aplicație sau de organizația dvs."</string> + <!-- no translation found for screenshot_edit (3510496440489019191) --> + <skip /> <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Închideți captura de ecran"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"Previzualizare a capturii de ecran"</string> <string name="screenrecord_name" msgid="2596401223859996572">"Recorder pentru ecran"</string> @@ -1069,6 +1071,8 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Se încarcă recomandările"</string> <string name="controls_media_title" msgid="1746947284862928133">"Media"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"Ascunde sesiunea actuală."</string> + <!-- no translation found for controls_media_active_session (1984383994625845642) --> + <skip /> <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Închideți"</string> <string name="controls_media_resume" msgid="1933520684481586053">"Reia"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"Setări"</string> diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml index 7f257a1c6b06..b51a5b2d1644 100644 --- a/packages/SystemUI/res/values-ru/strings.xml +++ b/packages/SystemUI/res/values-ru/strings.xml @@ -86,6 +86,8 @@ <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Попробуйте сделать скриншот снова."</string> <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Не удалось сохранить скриншот: недостаточно места."</string> <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Не удалось сделать скриншот: нет разрешения от приложения или организации."</string> + <!-- no translation found for screenshot_edit (3510496440489019191) --> + <skip /> <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Закрыть скриншот"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"Предварительный просмотр скриншота"</string> <string name="screenrecord_name" msgid="2596401223859996572">"Запись видео с экрана"</string> @@ -1075,6 +1077,8 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Загрузка рекомендаций…"</string> <string name="controls_media_title" msgid="1746947284862928133">"Медиа"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"Скрыть текущий сеанс?"</string> + <!-- no translation found for controls_media_active_session (1984383994625845642) --> + <skip /> <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Скрыть"</string> <string name="controls_media_resume" msgid="1933520684481586053">"Возобновить"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"Настройки"</string> diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml index ac743d318579..5d019e949a17 100644 --- a/packages/SystemUI/res/values-si/strings.xml +++ b/packages/SystemUI/res/values-si/strings.xml @@ -86,6 +86,8 @@ <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"තිර රුව නැවත ගැනීමට උත්සාහ කරන්න"</string> <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"සීමිත ගබඩා ඉඩ නිසා තිර රුව සුරැකිය නොහැකිය"</string> <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"තිර රූ ගැනීමට යෙදුම හෝ ඔබගේ සංවිධානය ඉඩ නොදේ"</string> + <!-- no translation found for screenshot_edit (3510496440489019191) --> + <skip /> <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"තිර රුව ඉවත ලන්න"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"තිර රූ පෙර දසුන"</string> <string name="screenrecord_name" msgid="2596401223859996572">"තිර රෙකෝඩරය"</string> @@ -1063,6 +1065,8 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"නිර්දේශ පූරණය කරමින්"</string> <string name="controls_media_title" msgid="1746947284862928133">"මාධ්ය"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"වත්මන් සැසිය සඟවන්න."</string> + <!-- no translation found for controls_media_active_session (1984383994625845642) --> + <skip /> <string name="controls_media_dismiss_button" msgid="9081375542265132213">"ඉවත ලන්න"</string> <string name="controls_media_resume" msgid="1933520684481586053">"නැවත පටන් ගන්න"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"සැකසීම්"</string> diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml index d29d1e99d635..88cc1a2cab60 100644 --- a/packages/SystemUI/res/values-sk/strings.xml +++ b/packages/SystemUI/res/values-sk/strings.xml @@ -86,6 +86,8 @@ <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Skúste snímku urobiť znova"</string> <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Snímka obrazovky sa nedá uložiť z dôvodu nedostatku miesta v úložisku"</string> <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Vytváranie snímok obrazovky je zakázané aplikáciou alebo vašou organizáciou"</string> + <!-- no translation found for screenshot_edit (3510496440489019191) --> + <skip /> <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Zavrieť snímku obrazovky"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"Ukážka snímky obrazovky"</string> <string name="screenrecord_name" msgid="2596401223859996572">"Rekordér obrazovky"</string> @@ -1075,6 +1077,8 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Načítavajú sa odporúčania"</string> <string name="controls_media_title" msgid="1746947284862928133">"Médiá"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"Skryť aktuálnu reláciu."</string> + <!-- no translation found for controls_media_active_session (1984383994625845642) --> + <skip /> <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Zavrieť"</string> <string name="controls_media_resume" msgid="1933520684481586053">"Pokračovať"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"Nastavenia"</string> diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml index 6cb0ccd2df9c..d1d14b26ebcf 100644 --- a/packages/SystemUI/res/values-sl/strings.xml +++ b/packages/SystemUI/res/values-sl/strings.xml @@ -86,6 +86,8 @@ <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Poskusite znova ustvariti posnetek zaslona"</string> <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Shranjevanje posnetka zaslona ni mogoče zaradi omejenega prostora za shranjevanje"</string> <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Aplikacija ali vaša organizacija ne dovoljuje posnetkov zaslona"</string> + <!-- no translation found for screenshot_edit (3510496440489019191) --> + <skip /> <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Opusti posnetek zaslona"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"Predogled posnetka zaslona"</string> <string name="screenrecord_name" msgid="2596401223859996572">"Snemalnik zaslona"</string> @@ -1075,6 +1077,8 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Nalaganje priporočil"</string> <string name="controls_media_title" msgid="1746947284862928133">"Predstavnost"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"Skrije trenutno sejo."</string> + <!-- no translation found for controls_media_active_session (1984383994625845642) --> + <skip /> <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Opusti"</string> <string name="controls_media_resume" msgid="1933520684481586053">"Nadaljuj"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"Nastavitve"</string> diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml index 154d53dd6d6f..8d79aa423691 100644 --- a/packages/SystemUI/res/values-sq/strings.xml +++ b/packages/SystemUI/res/values-sq/strings.xml @@ -86,6 +86,8 @@ <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Provo ta nxjerrësh përsëri pamjen e ekranit"</string> <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Pamja e ekranit nuk mund të ruhet për shkak të hapësirës ruajtëse të kufizuar"</string> <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Nxjerrja e pamjeve të ekranit nuk lejohet nga aplikacioni ose organizata jote."</string> + <!-- no translation found for screenshot_edit (3510496440489019191) --> + <skip /> <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Hiq pamjen e ekranit"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"Pamja paraprake e imazhit"</string> <string name="screenrecord_name" msgid="2596401223859996572">"Regjistruesi i ekranit"</string> @@ -1018,14 +1020,10 @@ <string name="accessibility_control_move_down" msgid="5390922476900974512">"Lëvize poshtë"</string> <string name="accessibility_control_move_left" msgid="8156206978511401995">"Lëvize majtas"</string> <string name="accessibility_control_move_right" msgid="8926821093629582888">"Lëvize djathtas"</string> - <!-- no translation found for magnification_mode_switch_description (2698364322069934733) --> - <skip /> - <!-- no translation found for magnification_mode_switch_state_full_screen (2882507327576770574) --> - <skip /> - <!-- no translation found for magnification_mode_switch_state_window (8597100249594076965) --> - <skip /> - <!-- no translation found for magnification_mode_switch_click_label (2786203505805898199) --> - <skip /> + <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Ndërrimi i zmadhimit"</string> + <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"Zmadho të gjithë ekranin"</string> + <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Zmadho një pjesë të ekranit"</string> + <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Ndërro"</string> <string name="quick_controls_title" msgid="6839108006171302273">"Kontrollet e pajisjes"</string> <string name="quick_controls_subtitle" msgid="1667408093326318053">"Shto kontrolle për pajisjet e tua të lidhura"</string> <string name="quick_controls_setup_title" msgid="8901436655997849822">"Konfiguro kontrollet e pajisjes"</string> @@ -1067,6 +1065,8 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Po ngarkon rekomandimet"</string> <string name="controls_media_title" msgid="1746947284862928133">"Media"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"Fshih sesionin aktual."</string> + <!-- no translation found for controls_media_active_session (1984383994625845642) --> + <skip /> <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Hiq"</string> <string name="controls_media_resume" msgid="1933520684481586053">"Vazhdo"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"Cilësimet"</string> diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml index 687a1bd93e4c..5affcdfb31a0 100644 --- a/packages/SystemUI/res/values-sr/strings.xml +++ b/packages/SystemUI/res/values-sr/strings.xml @@ -86,6 +86,8 @@ <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Пробајте да поново направите снимак екрана"</string> <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Чување снимка екрана није успело због ограниченог меморијског простора"</string> <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Апликација или организација не дозвољавају прављење снимака екрана"</string> + <!-- no translation found for screenshot_edit (3510496440489019191) --> + <skip /> <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Одбаците снимак екрана"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"Преглед снимка екрана"</string> <string name="screenrecord_name" msgid="2596401223859996572">"Снимач екрана"</string> @@ -1069,6 +1071,8 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Учитавају се препоруке"</string> <string name="controls_media_title" msgid="1746947284862928133">"Медији"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"Сакријте актуелну сесију."</string> + <!-- no translation found for controls_media_active_session (1984383994625845642) --> + <skip /> <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Одбаци"</string> <string name="controls_media_resume" msgid="1933520684481586053">"Настави"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"Подешавања"</string> diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml index 3dc89d883e5d..a51ed2879cb5 100644 --- a/packages/SystemUI/res/values-sv/strings.xml +++ b/packages/SystemUI/res/values-sv/strings.xml @@ -86,6 +86,8 @@ <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Testa att ta en skärmdump igen"</string> <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Det går inte att spara skärmdumpen eftersom lagringsutrymmet inte räcker"</string> <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Appen eller organisationen tillåter inte att du tar skärmdumpar"</string> + <!-- no translation found for screenshot_edit (3510496440489019191) --> + <skip /> <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Stäng skärmdump"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"Förhandsgranskning av skärmdump"</string> <string name="screenrecord_name" msgid="2596401223859996572">"Skärminspelare"</string> @@ -1063,6 +1065,8 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Rekommendationer läses in"</string> <string name="controls_media_title" msgid="1746947284862928133">"Media"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"Dölj den aktuella sessionen."</string> + <!-- no translation found for controls_media_active_session (1984383994625845642) --> + <skip /> <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Stäng"</string> <string name="controls_media_resume" msgid="1933520684481586053">"Återuppta"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"Inställningar"</string> diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml index 76c1cd4d3e4d..eab7f69b8914 100644 --- a/packages/SystemUI/res/values-sw/strings.xml +++ b/packages/SystemUI/res/values-sw/strings.xml @@ -86,6 +86,7 @@ <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Jaribu kupiga picha ya skrini tena"</string> <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Imeshindwa kuhifadhi picha ya skrini kwa sababu nafasi haitoshi"</string> <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Programu au shirika lako halikuruhusu kupiga picha za skrini"</string> + <string name="screenshot_edit" msgid="3510496440489019191">"Badilisha picha ya skrini"</string> <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Ondoa picha ya skrini"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"Onyesho la kukagua picha ya skrini"</string> <string name="screenrecord_name" msgid="2596401223859996572">"Kinasa Skrini"</string> @@ -1063,6 +1064,7 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Inapakia mapendekezo"</string> <string name="controls_media_title" msgid="1746947284862928133">"Maudhui"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"Ficha kipindi cha sasa."</string> + <string name="controls_media_active_session" msgid="1984383994625845642">"Huwezi kuficha kipindi cha sasa."</string> <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Ondoa"</string> <string name="controls_media_resume" msgid="1933520684481586053">"Endelea"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"Mipangilio"</string> diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml index 7e4ed357d541..3c1c12ac8ea0 100644 --- a/packages/SystemUI/res/values-ta/strings.xml +++ b/packages/SystemUI/res/values-ta/strings.xml @@ -86,6 +86,8 @@ <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"ஸ்கிரீன் ஷாட்டை மீண்டும் எடுக்க முயலவும்"</string> <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"போதுமான சேமிப்பிடம் இல்லாததால் ஸ்கிரீன்ஷாட்டைச் சேமிக்க முடியவில்லை"</string> <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"ஸ்கிரீன் ஷாட்டுகளை எடுப்பதை, ஆப்ஸ் அல்லது உங்கள் நிறுவனம் அனுமதிக்கவில்லை"</string> + <!-- no translation found for screenshot_edit (3510496440489019191) --> + <skip /> <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"ஸ்கிரீன்ஷாட்டை நிராகரி"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"ஸ்கிரீன்ஷாட்டின் மாதிரிக்காட்சி"</string> <string name="screenrecord_name" msgid="2596401223859996572">"ஸ்கிரீன் ரெக்கார்டர்"</string> @@ -1063,6 +1065,8 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"பரிந்துரைகளை ஏற்றுகிறது"</string> <string name="controls_media_title" msgid="1746947284862928133">"மீடியா"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"இந்த அமர்வை மறையுங்கள்."</string> + <!-- no translation found for controls_media_active_session (1984383994625845642) --> + <skip /> <string name="controls_media_dismiss_button" msgid="9081375542265132213">"மூடுக"</string> <string name="controls_media_resume" msgid="1933520684481586053">"தொடர்க"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"அமைப்புகள்"</string> diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml index f26c8b4de480..2680d22efb97 100644 --- a/packages/SystemUI/res/values-te/strings.xml +++ b/packages/SystemUI/res/values-te/strings.xml @@ -86,6 +86,8 @@ <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"స్క్రీన్షాట్ తీయడానికి మళ్లీ ప్రయత్నించండి"</string> <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"నిల్వ స్థలం పరిమితంగా ఉన్న కారణంగా స్క్రీన్షాట్ను సేవ్ చేయడం సాధ్యపడదు"</string> <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"స్క్రీన్షాట్లు తీయడానికి యాప్ లేదా మీ సంస్థ అనుమతించలేదు"</string> + <!-- no translation found for screenshot_edit (3510496440489019191) --> + <skip /> <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"స్క్రీన్షాట్ను మూసివేస్తుంది"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"స్క్రీన్షాట్ ప్రివ్యూ"</string> <string name="screenrecord_name" msgid="2596401223859996572">"స్క్రీన్ రికార్డర్"</string> @@ -1063,6 +1065,8 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"సిఫార్సులు లోడ్ అవుతున్నాయి"</string> <string name="controls_media_title" msgid="1746947284862928133">"మీడియా"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"ప్రస్తుత సెషన్ను దాచు."</string> + <!-- no translation found for controls_media_active_session (1984383994625845642) --> + <skip /> <string name="controls_media_dismiss_button" msgid="9081375542265132213">"విస్మరించు"</string> <string name="controls_media_resume" msgid="1933520684481586053">"కొనసాగించండి"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"సెట్టింగ్లు"</string> diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml index 9c61e5e45681..2e4682bd2541 100644 --- a/packages/SystemUI/res/values-th/strings.xml +++ b/packages/SystemUI/res/values-th/strings.xml @@ -86,6 +86,8 @@ <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"ลองบันทึกภาพหน้าจออีกครั้ง"</string> <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"บันทึกภาพหน้าจอไม่ได้เนื่องจากพื้นที่เก็บข้อมูลมีจำกัด"</string> <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"แอปหรือองค์กรของคุณไม่อนุญาตให้จับภาพหน้าจอ"</string> + <!-- no translation found for screenshot_edit (3510496440489019191) --> + <skip /> <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"ปิดภาพหน้าจอ"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"ตัวอย่างภาพหน้าจอ"</string> <string name="screenrecord_name" msgid="2596401223859996572">"โปรแกรมอัดหน้าจอ"</string> @@ -1018,7 +1020,7 @@ <string name="accessibility_control_move_down" msgid="5390922476900974512">"ย้ายลง"</string> <string name="accessibility_control_move_left" msgid="8156206978511401995">"ย้ายไปทางซ้าย"</string> <string name="accessibility_control_move_right" msgid="8926821093629582888">"ย้ายไปทางขวา"</string> - <string name="magnification_mode_switch_description" msgid="2698364322069934733">"เปลี่ยนการขยาย"</string> + <string name="magnification_mode_switch_description" msgid="2698364322069934733">"เปลี่ยนโหมดการขยาย"</string> <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"ขยายทั้งหน้าจอ"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"ขยายบางส่วนของหน้าจอ"</string> <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"เปลี่ยน"</string> @@ -1063,6 +1065,8 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"กำลังโหลดคำแนะนำ"</string> <string name="controls_media_title" msgid="1746947284862928133">"สื่อ"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"ซ่อนเซสชันปัจจุบัน"</string> + <!-- no translation found for controls_media_active_session (1984383994625845642) --> + <skip /> <string name="controls_media_dismiss_button" msgid="9081375542265132213">"ปิด"</string> <string name="controls_media_resume" msgid="1933520684481586053">"เล่นต่อ"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"การตั้งค่า"</string> diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml index be7bd06187c9..ae69c4fc3a04 100644 --- a/packages/SystemUI/res/values-tl/strings.xml +++ b/packages/SystemUI/res/values-tl/strings.xml @@ -86,6 +86,8 @@ <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Subukang kumuhang muli ng screenshot"</string> <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Hindi ma-save ang screenshot dahil sa limitadong espasyo ng storage"</string> <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Hindi pinahihintulutan ng app o ng iyong organisasyon ang pagkuha ng mga screenshot"</string> + <!-- no translation found for screenshot_edit (3510496440489019191) --> + <skip /> <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"I-dismiss ang screenshot"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"Preview ng screenshot"</string> <string name="screenrecord_name" msgid="2596401223859996572">"Recorder ng Screen"</string> @@ -1063,6 +1065,8 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Nilo-load ang rekomendasyon"</string> <string name="controls_media_title" msgid="1746947284862928133">"Media"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"Itago ang kasalukuyang session."</string> + <!-- no translation found for controls_media_active_session (1984383994625845642) --> + <skip /> <string name="controls_media_dismiss_button" msgid="9081375542265132213">"I-dismiss"</string> <string name="controls_media_resume" msgid="1933520684481586053">"Ituloy"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"Mga Setting"</string> diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml index 5d48836533ed..026517671be2 100644 --- a/packages/SystemUI/res/values-tr/strings.xml +++ b/packages/SystemUI/res/values-tr/strings.xml @@ -86,6 +86,8 @@ <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Tekrar ekran görüntüsü almayı deneyin"</string> <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Depolama alanı sınırlı olduğundan ekran görüntüsü kaydedilemiyor"</string> <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Uygulama veya kuruluşunuz, ekran görüntüsü alınmasına izin vermiyor."</string> + <!-- no translation found for screenshot_edit (3510496440489019191) --> + <skip /> <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Ekran görüntüsünü kapat"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"Ekran görüntüsü önizlemesi"</string> <string name="screenrecord_name" msgid="2596401223859996572">"Ekran Kaydedicisi"</string> @@ -1063,6 +1065,8 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Öneriler yükleniyor"</string> <string name="controls_media_title" msgid="1746947284862928133">"Medya"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"Mevcut oturumu gizle."</string> + <!-- no translation found for controls_media_active_session (1984383994625845642) --> + <skip /> <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Kapat"</string> <string name="controls_media_resume" msgid="1933520684481586053">"Devam ettir"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"Ayarlar"</string> diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml index 3b5c44b0afdc..c9fd5032609e 100644 --- a/packages/SystemUI/res/values-uk/strings.xml +++ b/packages/SystemUI/res/values-uk/strings.xml @@ -86,6 +86,8 @@ <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Спробуйте зробити знімок екрана ще раз"</string> <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Не вдалося зберегти знімок екрана через обмежений обсяг пам’яті"</string> <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Додаток або адміністратор вашої організації не дозволяють робити знімки екрана"</string> + <!-- no translation found for screenshot_edit (3510496440489019191) --> + <skip /> <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Закрити знімок екрана"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"Перегляд знімка екрана"</string> <string name="screenrecord_name" msgid="2596401223859996572">"Відеозапис екрана"</string> @@ -1075,6 +1077,8 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Завантаження рекомендацій"</string> <string name="controls_media_title" msgid="1746947284862928133">"Медіа"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"Приховати поточний сеанс."</string> + <!-- no translation found for controls_media_active_session (1984383994625845642) --> + <skip /> <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Закрити"</string> <string name="controls_media_resume" msgid="1933520684481586053">"Відновити"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"Налаштування"</string> diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml index ffba2bdf6afa..1bfa85394559 100644 --- a/packages/SystemUI/res/values-ur/strings.xml +++ b/packages/SystemUI/res/values-ur/strings.xml @@ -86,6 +86,8 @@ <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"دوبارہ اسکرین شاٹ لینے کی کوشش کریں"</string> <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"اسٹوریج کی محدود جگہ کی وجہ سے اسکرین شاٹ کو محفوظ نہیں کیا جا سکتا"</string> <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"ایپ یا آپ کی تنظیم کی جانب سے اسکرین شاٹس لینے کی اجازت نہیں ہے"</string> + <!-- no translation found for screenshot_edit (3510496440489019191) --> + <skip /> <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"اسکرین شاٹ برخاست کریں"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"اسکرین شاٹ کا پیش منظر"</string> <string name="screenrecord_name" msgid="2596401223859996572">"سکرین ریکارڈر"</string> @@ -1018,14 +1020,10 @@ <string name="accessibility_control_move_down" msgid="5390922476900974512">"نیچے منتقل کریں"</string> <string name="accessibility_control_move_left" msgid="8156206978511401995">"بائیں منتقل کریں"</string> <string name="accessibility_control_move_right" msgid="8926821093629582888">"دائیں منتقل کریں"</string> - <!-- no translation found for magnification_mode_switch_description (2698364322069934733) --> - <skip /> - <!-- no translation found for magnification_mode_switch_state_full_screen (2882507327576770574) --> - <skip /> - <!-- no translation found for magnification_mode_switch_state_window (8597100249594076965) --> - <skip /> - <!-- no translation found for magnification_mode_switch_click_label (2786203505805898199) --> - <skip /> + <string name="magnification_mode_switch_description" msgid="2698364322069934733">"میگنیفکیشن پر سوئچ کریں"</string> + <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"پوری اسکرین کو بڑا کریں"</string> + <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"اسکرین کا حصہ بڑا کریں"</string> + <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"سوئچ کریں"</string> <string name="quick_controls_title" msgid="6839108006171302273">"آلہ کے کنٹرولز"</string> <string name="quick_controls_subtitle" msgid="1667408093326318053">"اپنے منسلک آلات کے لیے کنٹرولز شامل کریں"</string> <string name="quick_controls_setup_title" msgid="8901436655997849822">"آلہ کے کنٹرولز سیٹ اپ کریں"</string> @@ -1067,6 +1065,8 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"تجاویز لوڈ ہو رہی ہیں"</string> <string name="controls_media_title" msgid="1746947284862928133">"میڈیا"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"موجودہ سیشن چھپائیں۔"</string> + <!-- no translation found for controls_media_active_session (1984383994625845642) --> + <skip /> <string name="controls_media_dismiss_button" msgid="9081375542265132213">"برخاست کریں"</string> <string name="controls_media_resume" msgid="1933520684481586053">"دوبارہ شروع کریں"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"ترتیبات"</string> diff --git a/packages/SystemUI/res/values-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml index 228b80d20027..332a26daee92 100644 --- a/packages/SystemUI/res/values-uz/strings.xml +++ b/packages/SystemUI/res/values-uz/strings.xml @@ -86,6 +86,7 @@ <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Qayta skrinshot olib ko‘ring"</string> <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Xotirada joy kamligi uchun skrinshot saqlanmadi"</string> <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Ilova yoki tashkilotingiz skrinshot olishni taqiqlagan"</string> + <string name="screenshot_edit" msgid="3510496440489019191">"Skrinshotni tahrirlash"</string> <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Skrinshotni yopish"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"Skrinshotga razm solish"</string> <string name="screenrecord_name" msgid="2596401223859996572">"Ekrandan yozib olish"</string> @@ -1018,7 +1019,7 @@ <string name="accessibility_control_move_down" msgid="5390922476900974512">"Pastga siljitish"</string> <string name="accessibility_control_move_left" msgid="8156206978511401995">"Chapga siljitish"</string> <string name="accessibility_control_move_right" msgid="8926821093629582888">"Oʻngga siljitish"</string> - <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Kattalashtirishni almashtirish"</string> + <string name="magnification_mode_switch_description" msgid="2698364322069934733">"Kattalashtirish rejimini almashtirish"</string> <string name="magnification_mode_switch_state_full_screen" msgid="2882507327576770574">"Butun ekranni kattalashtirish"</string> <string name="magnification_mode_switch_state_window" msgid="8597100249594076965">"Ekran qismini kattalashtirish"</string> <string name="magnification_mode_switch_click_label" msgid="2786203505805898199">"Almashtirish"</string> @@ -1063,6 +1064,7 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Tavsiyalar yuklanmoqda"</string> <string name="controls_media_title" msgid="1746947284862928133">"Media"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"Joriy seans berkitilsin."</string> + <string name="controls_media_active_session" msgid="1984383994625845642">"Joriy seans berkilmaydi."</string> <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Yopish"</string> <string name="controls_media_resume" msgid="1933520684481586053">"Davom etish"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"Sozlamalar"</string> diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml index 849223651fd7..947bd435c777 100644 --- a/packages/SystemUI/res/values-vi/strings.xml +++ b/packages/SystemUI/res/values-vi/strings.xml @@ -86,6 +86,8 @@ <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Hãy thử chụp lại màn hình"</string> <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Không thể lưu ảnh chụp màn hình do giới hạn dung lượng bộ nhớ"</string> <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Ứng dụng hoặc tổ chức của bạn không cho phép chụp ảnh màn hình"</string> + <!-- no translation found for screenshot_edit (3510496440489019191) --> + <skip /> <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Đóng ảnh chụp màn hình"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"Xem trước ảnh chụp màn hình"</string> <string name="screenrecord_name" msgid="2596401223859996572">"Trình ghi màn hình"</string> @@ -1063,6 +1065,8 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Đang tải các đề xuất"</string> <string name="controls_media_title" msgid="1746947284862928133">"Nội dung nghe nhìn"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"Ẩn phiên hiện tại."</string> + <!-- no translation found for controls_media_active_session (1984383994625845642) --> + <skip /> <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Đóng"</string> <string name="controls_media_resume" msgid="1933520684481586053">"Tiếp tục"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"Cài đặt"</string> diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml index 72e7640f6f54..730fa81a5975 100644 --- a/packages/SystemUI/res/values-zh-rCN/strings.xml +++ b/packages/SystemUI/res/values-zh-rCN/strings.xml @@ -86,6 +86,8 @@ <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"请再次尝试截屏"</string> <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"由于存储空间有限,无法保存屏幕截图"</string> <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"此应用或您所在的单位不允许进行屏幕截图"</string> + <!-- no translation found for screenshot_edit (3510496440489019191) --> + <skip /> <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"关闭屏幕截图"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"屏幕截图预览"</string> <string name="screenrecord_name" msgid="2596401223859996572">"屏幕录制器"</string> @@ -1063,6 +1065,8 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"正在加载推荐内容"</string> <string name="controls_media_title" msgid="1746947284862928133">"媒体"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"隐藏当前会话。"</string> + <!-- no translation found for controls_media_active_session (1984383994625845642) --> + <skip /> <string name="controls_media_dismiss_button" msgid="9081375542265132213">"关闭"</string> <string name="controls_media_resume" msgid="1933520684481586053">"继续播放"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"设置"</string> diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml index c3dc8d2b17c6..42361079a9bb 100644 --- a/packages/SystemUI/res/values-zh-rHK/strings.xml +++ b/packages/SystemUI/res/values-zh-rHK/strings.xml @@ -86,6 +86,8 @@ <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"請再嘗試拍攝螢幕擷取畫面"</string> <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"由於儲存空間有限,因此無法儲存螢幕擷取畫面"</string> <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"應用程式或您的機構不允許擷取螢幕畫面"</string> + <!-- no translation found for screenshot_edit (3510496440489019191) --> + <skip /> <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"關閉螢幕截圖"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"螢幕截圖預覽"</string> <string name="screenrecord_name" msgid="2596401223859996572">"螢幕畫面錄影工具"</string> @@ -1063,6 +1065,8 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"正在載入建議"</string> <string name="controls_media_title" msgid="1746947284862928133">"媒體"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"隱藏目前的工作階段。"</string> + <!-- no translation found for controls_media_active_session (1984383994625845642) --> + <skip /> <string name="controls_media_dismiss_button" msgid="9081375542265132213">"關閉"</string> <string name="controls_media_resume" msgid="1933520684481586053">"繼續播放"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"設定"</string> diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml index 02fc24f41abb..c7407ef03730 100644 --- a/packages/SystemUI/res/values-zh-rTW/strings.xml +++ b/packages/SystemUI/res/values-zh-rTW/strings.xml @@ -86,6 +86,8 @@ <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"請再次嘗試拍攝螢幕截圖"</string> <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"由於儲存空間有限,因此無法儲存螢幕截圖"</string> <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"這個應用程式或貴機構不允許擷取螢幕畫面"</string> + <!-- no translation found for screenshot_edit (3510496440489019191) --> + <skip /> <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"關閉螢幕截圖"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"螢幕截圖預覽"</string> <string name="screenrecord_name" msgid="2596401223859996572">"螢幕錄影器"</string> @@ -1063,6 +1065,8 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"正在載入建議控制項"</string> <string name="controls_media_title" msgid="1746947284862928133">"媒體"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"隱藏目前的工作階段。"</string> + <!-- no translation found for controls_media_active_session (1984383994625845642) --> + <skip /> <string name="controls_media_dismiss_button" msgid="9081375542265132213">"關閉"</string> <string name="controls_media_resume" msgid="1933520684481586053">"繼續播放"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"設定"</string> diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml index 8eb86bcb6b75..dd7f6f65b86c 100644 --- a/packages/SystemUI/res/values-zu/strings.xml +++ b/packages/SystemUI/res/values-zu/strings.xml @@ -86,6 +86,8 @@ <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Zama ukuthatha isithombe-skrini futhi"</string> <string name="screenshot_failed_to_save_text" msgid="8344173457344027501">"Ayikwazi ukulondoloza isithombe-skrini ngenxa yesikhala sesitoreji esikhawulelwe"</string> <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Ukuthatha izithombe-skrini akuvunyelwe uhlelo lokusebenza noma inhlangano yakho"</string> + <!-- no translation found for screenshot_edit (3510496440489019191) --> + <skip /> <string name="screenshot_dismiss_ui_description" msgid="934736855340147968">"Cashisa isithombe-skrini"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"Ukubuka kuqala isithombe-skrini"</string> <string name="screenrecord_name" msgid="2596401223859996572">"Irekhoda yesikrini"</string> @@ -1063,6 +1065,8 @@ <string name="controls_seeding_in_progress" msgid="3033855341410264148">"Ilayisha izincomo"</string> <string name="controls_media_title" msgid="1746947284862928133">"Imidiya"</string> <string name="controls_media_close_session" msgid="3957093425905475065">"Fihla iseshini yamanje."</string> + <!-- no translation found for controls_media_active_session (1984383994625845642) --> + <skip /> <string name="controls_media_dismiss_button" msgid="9081375542265132213">"Cashisa"</string> <string name="controls_media_resume" msgid="1933520684481586053">"Qalisa kabusha"</string> <string name="controls_media_settings_button" msgid="5815790345117172504">"Izilungiselelo"</string> diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml index 3d7b779c8a46..6df8b4e733bb 100644 --- a/packages/SystemUI/res/values/colors.xml +++ b/packages/SystemUI/res/values/colors.xml @@ -54,6 +54,8 @@ <color name="global_actions_emergency_background">@color/GM2_red_400</color> <color name="global_actions_emergency_text">@color/GM2_grey_100</color> + <color name="global_actions_shutdown_ui_text">@color/control_primary_text</color> + <!-- Tint color for the content on the notification overflow card. --> <color name="keyguard_overflow_content_color">#ff686868</color> diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index 7cbbaf9a00ac..4b1ed0a25c90 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -323,7 +323,7 @@ <dimen name="screenshot_action_container_padding_right">8dp</dimen> <!-- Radius of the chip background on global screenshot actions --> <dimen name="screenshot_button_corner_radius">20dp</dimen> - <dimen name="screenshot_action_chip_margin_right">8dp</dimen> + <dimen name="screenshot_action_chip_margin_start">8dp</dimen> <dimen name="screenshot_action_chip_margin_vertical">10dp</dimen> <dimen name="screenshot_action_chip_padding_vertical">7dp</dimen> <dimen name="screenshot_action_chip_icon_size">18dp</dimen> @@ -1217,7 +1217,7 @@ <!-- Interior padding of the message bubble --> <dimen name="bubble_message_padding">4dp</dimen> <!-- Offset between bubbles in their stacked position. --> - <dimen name="bubble_stack_offset">5dp</dimen> + <dimen name="bubble_stack_offset">10dp</dimen> <!-- How far offscreen the bubble stack rests. Cuts off padding and part of icon bitmap. --> <dimen name="bubble_stack_offscreen">9dp</dimen> <!-- How far down the screen the stack starts. --> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index d1fb6cd4f209..e2ba615e84e6 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -233,6 +233,8 @@ <!-- 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> + <!-- Content description indicating that tapping the element will allow editing the screenshot [CHAR LIMIT=NONE] --> + <string name="screenshot_edit">Edit screenshot</string> <!-- Content description indicating that tapping a button will dismiss the screenshots UI [CHAR LIMIT=NONE] --> <string name="screenshot_dismiss_ui_description">Dismiss screenshot</string> <!-- Content description indicating that the view is a preview of the screenshot that was just taken [CHAR LIMIT=NONE] --> @@ -2782,6 +2784,8 @@ <string name="controls_media_title">Media</string> <!-- Explanation for closing controls associated with a specific media session [CHAR_LIMIT=NONE] --> <string name="controls_media_close_session">Hide the current session.</string> + <!-- Explanation that controls associated with a specific media session are active [CHAR_LIMIT=NONE] --> + <string name="controls_media_active_session">Current session cannot be hidden.</string> <!-- Label for a button that will hide media controls [CHAR_LIMIT=30] --> <string name="controls_media_dismiss_button">Dismiss</string> <!-- Label for button to resume media playback [CHAR_LIMIT=NONE] --> diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java index b81ffb766440..42380198cf17 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java @@ -20,10 +20,6 @@ import static android.app.ActivityManager.LOCK_TASK_MODE_LOCKED; import static android.app.ActivityManager.LOCK_TASK_MODE_NONE; import static android.app.ActivityManager.LOCK_TASK_MODE_PINNED; import static android.app.ActivityManager.RECENT_IGNORE_UNAVAILABLE; -import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; -import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; -import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY; -import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import android.annotation.NonNull; import android.app.Activity; @@ -58,7 +54,6 @@ import android.view.RemoteAnimationTarget; import com.android.internal.app.IVoiceInteractionManagerService; import com.android.systemui.shared.recents.model.Task; -import com.android.systemui.shared.recents.model.Task.TaskKey; import com.android.systemui.shared.recents.model.ThumbnailData; import java.util.ArrayList; @@ -226,6 +221,22 @@ public class ActivityManagerWrapper { public void startRecentsActivity(Intent intent, long eventTime, final RecentsAnimationListener animationHandler, final Consumer<Boolean> resultCallback, Handler resultCallbackHandler) { + boolean result = startRecentsActivity(intent, eventTime, animationHandler); + if (resultCallback != null) { + resultCallbackHandler.post(new Runnable() { + @Override + public void run() { + resultCallback.accept(result); + } + }); + } + } + + /** + * Starts the recents activity. The caller should manage the thread on which this is called. + */ + public boolean startRecentsActivity( + Intent intent, long eventTime, RecentsAnimationListener animationHandler) { try { IRecentsAnimationRunner runner = null; if (animationHandler != null) { @@ -257,23 +268,9 @@ public class ActivityManagerWrapper { }; } ActivityTaskManager.getService().startRecentsActivity(intent, eventTime, runner); - if (resultCallback != null) { - resultCallbackHandler.post(new Runnable() { - @Override - public void run() { - resultCallback.accept(true); - } - }); - } + return true; } catch (Exception e) { - if (resultCallback != null) { - resultCallbackHandler.post(new Runnable() { - @Override - public void run() { - resultCallback.accept(false); - } - }); - } + return false; } } @@ -291,53 +288,17 @@ public class ActivityManagerWrapper { /** * Starts a task from Recents. * - * @see {@link #startActivityFromRecentsAsync(TaskKey, ActivityOptions, int, int, Consumer, Handler)} - */ - public void startActivityFromRecentsAsync(Task.TaskKey taskKey, ActivityOptions options, - Consumer<Boolean> resultCallback, Handler resultCallbackHandler) { - startActivityFromRecentsAsync(taskKey, options, WINDOWING_MODE_UNDEFINED, - ACTIVITY_TYPE_UNDEFINED, resultCallback, resultCallbackHandler); - } - - /** - * Starts a task from Recents. - * * @param resultCallback The result success callback * @param resultCallbackHandler The handler to receive the result callback */ - public void startActivityFromRecentsAsync(final Task.TaskKey taskKey, ActivityOptions options, - int windowingMode, int activityType, final Consumer<Boolean> resultCallback, - final Handler resultCallbackHandler) { - if (taskKey.windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) { - // We show non-visible docked tasks in Recents, but we always want to launch - // them in the fullscreen stack. - if (options == null) { - options = ActivityOptions.makeBasic(); - } - options.setLaunchWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY); - } else if (windowingMode != WINDOWING_MODE_UNDEFINED - || activityType != ACTIVITY_TYPE_UNDEFINED) { - if (options == null) { - options = ActivityOptions.makeBasic(); - } - options.setLaunchWindowingMode(windowingMode); - options.setLaunchActivityType(activityType); - } - final ActivityOptions finalOptions = options; - - - boolean result = false; - try { - result = startActivityFromRecents(taskKey.id, finalOptions); - } catch (Exception e) { - // Fall through - } - final boolean finalResult = result; + public void startActivityFromRecentsAsync(Task.TaskKey taskKey, ActivityOptions options, + Consumer<Boolean> resultCallback, Handler resultCallbackHandler) { + final boolean result = startActivityFromRecents(taskKey, options); if (resultCallback != null) { resultCallbackHandler.post(new Runnable() { @Override public void run() { - resultCallback.accept(finalResult); + resultCallback.accept(result); } }); } @@ -346,6 +307,14 @@ public class ActivityManagerWrapper { /** * Starts a task from Recents synchronously. */ + public boolean startActivityFromRecents(Task.TaskKey taskKey, ActivityOptions options) { + ActivityOptionsCompat.addTaskInfo(options, taskKey); + return startActivityFromRecents(taskKey.id, options); + } + + /** + * Starts a task from Recents synchronously. + */ public boolean startActivityFromRecents(int taskId, ActivityOptions options) { try { Bundle optsBundle = options == null ? null : options.toBundle(); 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 0db4faf9e493..1a71f119419c 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 @@ -26,6 +26,8 @@ import android.app.ActivityOptions; import android.content.Context; import android.os.Handler; +import com.android.systemui.shared.recents.model.Task; + /** * Wrapper around internal ActivityOptions creation. */ @@ -94,4 +96,15 @@ public abstract class ActivityOptionsCompat { opts.setSourceInfo(ActivityOptions.SourceInfo.TYPE_LAUNCHER, uptimeMillis); return opts; } + + /** + * Sets Task specific information to the activity options + */ + public static void addTaskInfo(ActivityOptions opts, Task.TaskKey taskKey) { + if (taskKey.windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) { + // We show non-visible docked tasks in Recents, but we always want to launch + // them in the fullscreen stack. + opts.setLaunchWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY); + } + } } diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/LatencyTrackerCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/LatencyTrackerCompat.java index 0d5933e5f599..bf4fb0b6517e 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/LatencyTrackerCompat.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/LatencyTrackerCompat.java @@ -28,7 +28,18 @@ public class LatencyTrackerCompat { return LatencyTracker.isEnabled(context); } + /** + * @see LatencyTracker + * @deprecated Please use {@link LatencyTrackerCompat#logToggleRecents(Context, int)} instead. + */ + @Deprecated public static void logToggleRecents(int duration) { - LatencyTracker.logAction(LatencyTracker.ACTION_TOGGLE_RECENTS, duration); + LatencyTracker.logActionDeprecated(LatencyTracker.ACTION_TOGGLE_RECENTS, duration, false); + } + + /** @see LatencyTracker */ + public static void logToggleRecents(Context context, int duration) { + LatencyTracker.getInstance(context).logAction(LatencyTracker.ACTION_TOGGLE_RECENTS, + duration); } }
\ No newline at end of file diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java index 86129e0e204e..70021b6f3d45 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java @@ -65,7 +65,7 @@ public class SyncRtSurfaceTransactionApplierCompat { public SyncRtSurfaceTransactionApplierCompat(View targetView) { mTargetViewRootImpl = targetView != null ? targetView.getViewRootImpl() : null; mBarrierSurfaceControl = mTargetViewRootImpl != null - ? mTargetViewRootImpl.getRenderSurfaceControl() : null; + ? mTargetViewRootImpl.getSurfaceControl() : null; mApplyHandler = new Handler(new Callback() { @Override diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ViewRootImplCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ViewRootImplCompat.java index 73783ae7ece2..4a28d56a41e1 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ViewRootImplCompat.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ViewRootImplCompat.java @@ -34,10 +34,6 @@ public class ViewRootImplCompat { } public SurfaceControl getRenderSurfaceControl() { - return mViewRoot == null ? null : mViewRoot.getRenderSurfaceControl(); - } - - public SurfaceControl getSurfaceControl() { return mViewRoot == null ? null : mViewRoot.getSurfaceControl(); } 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 5122f6cf31a1..5b41d5f90975 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 @@ -55,8 +55,6 @@ public class WindowManagerWrapper { WindowManager.TRANSIT_WALLPAPER_INTRA_CLOSE; public static final int TRANSIT_TASK_OPEN_BEHIND = WindowManager.TRANSIT_TASK_OPEN_BEHIND; 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; @@ -102,6 +100,18 @@ public class WindowManagerWrapper { } /** + * Sets if app requested fixed orientation should be ignored for given displayId. + */ + public void setIgnoreOrientationRequest(int displayId, boolean ignoreOrientationRequest) { + try { + WindowManagerGlobal.getWindowManagerService().setIgnoreOrientationRequest( + displayId, ignoreOrientationRequest); + } catch (RemoteException e) { + Log.e(TAG, "Failed to setIgnoreOrientationRequest()", e); + } + } + + /** * @return the stable insets for the primary display. */ public void getStableInsets(Rect outStableInsets) { diff --git a/packages/SystemUI/src/com/android/keyguard/GradientTextClock.java b/packages/SystemUI/src/com/android/keyguard/GradientTextClock.java new file mode 100644 index 000000000000..b59638802756 --- /dev/null +++ b/packages/SystemUI/src/com/android/keyguard/GradientTextClock.java @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.keyguard; + +import android.content.Context; +import android.graphics.LinearGradient; +import android.graphics.Shader; +import android.util.AttributeSet; +import android.widget.TextClock; + +/** + * Displays the time with the hour positioned above the minutes. (ie: 09 above 30 is 9:30) + * The time's text color is a gradient that changes its colors based on its controller. + */ +public class GradientTextClock extends TextClock { + private int[] mGradientColors; + private float[] mPositions; + + public GradientTextClock(Context context) { + this(context, null, 0, 0); + } + + public GradientTextClock(Context context, AttributeSet attrs) { + this(context, attrs, 0, 0); + } + + public GradientTextClock(Context context, AttributeSet attrs, int defStyleAttr) { + this(context, attrs, defStyleAttr, 0); + } + + public GradientTextClock(Context context, AttributeSet attrs, int defStyleAttr, + int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + } + + @Override + public void onAttachedToWindow() { + super.onAttachedToWindow(); + addOnLayoutChangeListener(mOnLayoutChangeListener); + } + + @Override + public void onDetachedFromWindow() { + super.onDetachedFromWindow(); + removeOnLayoutChangeListener(mOnLayoutChangeListener); + } + + @Override + public void refreshTime() { + updatePaint(); + super.refreshTime(); + } + + @Override + public void setFormat12Hour(CharSequence format) { + super.setFormat12Hour(FORMAT_12); + } + + @Override + public void setFormat24Hour(CharSequence format) { + super.setFormat24Hour(FORMAT_24); + } + + public void setGradientColors(int[] colors) { + mGradientColors = colors; + } + + public void setColorPositions(float[] positions) { + mPositions = positions; + } + + private void updatePaint() { + getPaint().setShader( + new LinearGradient( + getX(), getY(), getX(), getMeasuredHeight() + getY(), + mGradientColors, mPositions, Shader.TileMode.REPEAT)); + } + + private final OnLayoutChangeListener mOnLayoutChangeListener = + (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> { + if (bottom != oldBottom || top != oldTop) { + updatePaint(); + } + }; + + public static final CharSequence FORMAT_12 = "hh\nmm"; + public static final CharSequence FORMAT_24 = "HH\nmm"; +} diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java index 272954df6dd6..cf363cc649f0 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java @@ -72,6 +72,16 @@ public class KeyguardClockSwitch extends RelativeLayout { private TextClock mClockViewBold; /** + * Gradient clock for usage when mode != KeyguardUpdateMonitor.LOCK_SCREEN_MODE_NORMAL. + */ + private TimeBasedColorsClockController mNewLockscreenClockViewController; + + /** + * Frame for clock when mode != KeyguardUpdateMonitor.LOCK_SCREEN_MODE_NORMAL. + */ + private FrameLayout mNewLockscreenClockFrame; + + /** * Frame for default and custom clock. */ private FrameLayout mSmallClockFrame; @@ -137,23 +147,22 @@ public class KeyguardClockSwitch extends RelativeLayout { mLockScreenMode = mode; RelativeLayout.LayoutParams statusAreaLP = (RelativeLayout.LayoutParams) mKeyguardStatusArea.getLayoutParams(); - RelativeLayout.LayoutParams clockLP = (RelativeLayout.LayoutParams) - mSmallClockFrame.getLayoutParams(); if (mode == KeyguardUpdateMonitor.LOCK_SCREEN_MODE_LAYOUT_1) { + mSmallClockFrame.setVisibility(GONE); + mNewLockscreenClockFrame.setVisibility(VISIBLE); + mNewLockscreenClockViewController.init(); + statusAreaLP.removeRule(RelativeLayout.BELOW); - statusAreaLP.addRule(RelativeLayout.LEFT_OF, R.id.clock_view); + statusAreaLP.addRule(RelativeLayout.LEFT_OF, R.id.new_lockscreen_clock_view); statusAreaLP.addRule(RelativeLayout.ALIGN_PARENT_START); - - clockLP.addRule(RelativeLayout.ALIGN_PARENT_END); - clockLP.width = ViewGroup.LayoutParams.WRAP_CONTENT; } else { + mSmallClockFrame.setVisibility(VISIBLE); + mNewLockscreenClockFrame.setVisibility(GONE); + statusAreaLP.removeRule(RelativeLayout.LEFT_OF); statusAreaLP.removeRule(RelativeLayout.ALIGN_PARENT_START); statusAreaLP.addRule(RelativeLayout.BELOW, R.id.clock_view); - - clockLP.removeRule(RelativeLayout.ALIGN_PARENT_END); - clockLP.width = ViewGroup.LayoutParams.MATCH_PARENT; } requestLayout(); @@ -164,6 +173,9 @@ public class KeyguardClockSwitch extends RelativeLayout { super.onFinishInflate(); mClockView = findViewById(R.id.default_clock_view); mClockViewBold = findViewById(R.id.default_clock_view_bold); + mNewLockscreenClockFrame = findViewById(R.id.new_lockscreen_clock_view); + mNewLockscreenClockViewController = + new TimeBasedColorsClockController(findViewById(R.id.gradient_clock_view)); mSmallClockFrame = findViewById(R.id.clock_view); mKeyguardStatusArea = findViewById(R.id.keyguard_status_area); } @@ -336,6 +348,7 @@ public class KeyguardClockSwitch extends RelativeLayout { * Refresh the time of the clock, due to either time tick broadcast or doze time tick alarm. */ public void refresh() { + mNewLockscreenClockViewController.refreshTime(System.currentTimeMillis()); mClockView.refreshTime(); mClockViewBold.refreshTime(); if (mClockPlugin != null) { diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java index fe5fcc6fd632..1562444f6f87 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java @@ -17,24 +17,32 @@ package com.android.keyguard; import android.app.WallpaperManager; -import android.view.View; +import android.content.res.Resources; +import android.text.format.DateFormat; +import android.util.TypedValue; import android.view.ViewGroup; import com.android.internal.colorextraction.ColorExtractor; import com.android.keyguard.clock.ClockManager; +import com.android.systemui.R; import com.android.systemui.colorextraction.SysuiColorExtractor; +import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.plugins.ClockPlugin; import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.util.ViewController; + +import java.util.Locale; +import java.util.TimeZone; import javax.inject.Inject; /** * Injectable controller for {@link KeyguardClockSwitch}. */ -public class KeyguardClockSwitchController { +public class KeyguardClockSwitchController extends ViewController<KeyguardClockSwitch> { private static final boolean CUSTOM_CLOCKS_ENABLED = true; - private final KeyguardClockSwitch mView; + private final Resources mResources; private final StatusBarStateController mStatusBarStateController; private final SysuiColorExtractor mColorExtractor; private final ClockManager mClockManager; @@ -65,35 +73,15 @@ public class KeyguardClockSwitchController { private ClockManager.ClockChangedListener mClockChangedListener = this::setClockPlugin; - private final View.OnAttachStateChangeListener mOnAttachStateChangeListener = - new View.OnAttachStateChangeListener() { - @Override - public void onViewAttachedToWindow(View v) { - if (CUSTOM_CLOCKS_ENABLED) { - mClockManager.addOnClockChangedListener(mClockChangedListener); - } - mStatusBarStateController.addCallback(mStateListener); - mColorExtractor.addOnColorsChangedListener(mColorsListener); - mView.updateColors(getGradientColors()); - } - - @Override - public void onViewDetachedFromWindow(View v) { - if (CUSTOM_CLOCKS_ENABLED) { - mClockManager.removeOnClockChangedListener(mClockChangedListener); - } - mStatusBarStateController.removeCallback(mStateListener); - mColorExtractor.removeOnColorsChangedListener(mColorsListener); - mView.setClockPlugin(null, mStatusBarStateController.getState()); - } - }; - @Inject - public KeyguardClockSwitchController(KeyguardClockSwitch keyguardClockSwitch, + public KeyguardClockSwitchController( + KeyguardClockSwitch keyguardClockSwitch, + @Main Resources resources, StatusBarStateController statusBarStateController, SysuiColorExtractor colorExtractor, ClockManager clockManager, KeyguardSliceViewController keyguardSliceViewController) { - mView = keyguardClockSwitch; + super(keyguardClockSwitch); + mResources = resources; mStatusBarStateController = statusBarStateController; mColorExtractor = colorExtractor; mClockManager = clockManager; @@ -103,13 +91,39 @@ public class KeyguardClockSwitchController { /** * Attach the controller to the view it relates to. */ + @Override public void init() { - if (mView.isAttachedToWindow()) { - mOnAttachStateChangeListener.onViewAttachedToWindow(mView); + super.init(); + mKeyguardSliceViewController.init(); + } + + @Override + protected void onViewAttached() { + if (CUSTOM_CLOCKS_ENABLED) { + mClockManager.addOnClockChangedListener(mClockChangedListener); + } + refreshFormat(); + mStatusBarStateController.addCallback(mStateListener); + mColorExtractor.addOnColorsChangedListener(mColorsListener); + mView.updateColors(getGradientColors()); + } + + @Override + protected void onViewDetached() { + if (CUSTOM_CLOCKS_ENABLED) { + mClockManager.removeOnClockChangedListener(mClockChangedListener); } - mView.addOnAttachStateChangeListener(mOnAttachStateChangeListener); + mStatusBarStateController.removeCallback(mStateListener); + mColorExtractor.removeOnColorsChangedListener(mColorsListener); + mView.setClockPlugin(null, mStatusBarStateController.getState()); + } - mKeyguardSliceViewController.init(); + /** + * Updates clock's text + */ + public void onDensityOrFontScaleChanged() { + mView.setTextSize(TypedValue.COMPLEX_UNIT_PX, + mResources.getDimensionPixelSize(R.dimen.widget_big_font_size)); } /** @@ -119,6 +133,61 @@ public class KeyguardClockSwitchController { mView.setBigClockContainer(bigClockContainer, mStatusBarStateController.getState()); } + /** + * Set whether or not the lock screen is showing notifications. + */ + public void setHasVisibleNotifications(boolean hasVisibleNotifications) { + mView.setHasVisibleNotifications(hasVisibleNotifications); + } + + /** + * If we're presenting a custom clock of just the default one. + */ + public boolean hasCustomClock() { + return mView.hasCustomClock(); + } + + /** + * Get the clock text size. + */ + public float getClockTextSize() { + return mView.getTextSize(); + } + + /** + * Returns the preferred Y position of the clock. + * + * @param totalHeight The height available to position the clock. + * @return Y position of clock. + */ + public int getClockPreferredY(int totalHeight) { + return mView.getPreferredY(totalHeight); + } + + /** + * Refresh clock. Called in response to TIME_TICK broadcasts. + */ + void refresh() { + mView.refresh(); + } + + /** + * Update lockscreen mode that may change clock display. + */ + void updateLockScreenMode(int mode) { + mView.updateLockScreenMode(mode); + } + + void updateTimeZone(TimeZone timeZone) { + mView.onTimeZoneChanged(timeZone); + } + + void refreshFormat() { + Patterns.update(mResources); + mView.setFormat12Hour(Patterns.sClockView12); + mView.setFormat24Hour(Patterns.sClockView24); + } + private void setClockPlugin(ClockPlugin plugin) { mView.setClockPlugin(plugin, mStatusBarStateController.getState()); } @@ -126,4 +195,35 @@ public class KeyguardClockSwitchController { private ColorExtractor.GradientColors getGradientColors() { return mColorExtractor.getColors(WallpaperManager.FLAG_LOCK); } + + // DateFormat.getBestDateTimePattern is extremely expensive, and refresh is called often. + // This is an optimization to ensure we only recompute the patterns when the inputs change. + private static final class Patterns { + static String sClockView12; + static String sClockView24; + static String sCacheKey; + + static void update(Resources res) { + final Locale locale = Locale.getDefault(); + final String clockView12Skel = res.getString(R.string.clock_12hr_format); + final String clockView24Skel = res.getString(R.string.clock_24hr_format); + final String key = locale.toString() + clockView12Skel + clockView24Skel; + if (key.equals(sCacheKey)) return; + + sClockView12 = DateFormat.getBestDateTimePattern(locale, clockView12Skel); + // CLDR insists on adding an AM/PM indicator even though it wasn't in the skeleton + // format. The following code removes the AM/PM indicator if we didn't want it. + if (!clockView12Skel.contains("a")) { + sClockView12 = sClockView12.replaceAll("a", "").trim(); + } + + sClockView24 = DateFormat.getBestDateTimePattern(locale, clockView24Skel); + + // Use fancy colon. + sClockView24 = sClockView24.replace(':', '\uee01'); + sClockView12 = sClockView12.replace(':', '\uee01'); + + sCacheKey = key; + } + } } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java index 36d5543f1c01..901a7360f311 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java @@ -20,7 +20,7 @@ import static android.view.Display.DEFAULT_DISPLAY; import android.app.Presentation; import android.content.Context; import android.graphics.Color; -import android.graphics.Point; +import android.graphics.Rect; import android.hardware.display.DisplayManager; import android.media.MediaRouter; import android.media.MediaRouter.RouteInfo; @@ -127,7 +127,7 @@ public class KeyguardDisplayManager { Presentation presentation = mPresentations.get(displayId); if (presentation == null) { final Presentation newPresentation = new KeyguardPresentation(mContext, display, - mKeyguardStatusViewComponentFactory, LayoutInflater.from(mContext)); + mKeyguardStatusViewComponentFactory); newPresentation.setOnDismissListener(dialog -> { if (newPresentation.equals(mPresentations.get(displayId))) { mPresentations.remove(displayId); @@ -245,7 +245,6 @@ public class KeyguardDisplayManager { private static final int VIDEO_SAFE_REGION = 80; // Percentage of display width & height private static final int MOVE_CLOCK_TIMEOUT = 10000; // 10s private final KeyguardStatusViewComponent.Factory mKeyguardStatusViewComponentFactory; - private final LayoutInflater mLayoutInflater; private KeyguardClockSwitchController mKeyguardClockSwitchController; private View mClock; private int mUsableWidth; @@ -264,18 +263,16 @@ public class KeyguardDisplayManager { }; KeyguardPresentation(Context context, Display display, - KeyguardStatusViewComponent.Factory keyguardStatusViewComponentFactory, - LayoutInflater layoutInflater) { - super(context, display, R.style.Theme_SystemUI_KeyguardPresentation); + KeyguardStatusViewComponent.Factory keyguardStatusViewComponentFactory) { + super(context, display, R.style.Theme_SystemUI_KeyguardPresentation, + WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG); mKeyguardStatusViewComponentFactory = keyguardStatusViewComponentFactory; - mLayoutInflater = layoutInflater; - getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG); setCancelable(false); } @Override public void cancel() { - // Do not allow anything to cancel KeyguardPresetation except KeyguardDisplayManager. + // Do not allow anything to cancel KeyguardPresentation except KeyguardDisplayManager. } @Override @@ -287,14 +284,15 @@ public class KeyguardDisplayManager { protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - Point p = new Point(); - getDisplay().getSize(p); - mUsableWidth = VIDEO_SAFE_REGION * p.x/100; - mUsableHeight = VIDEO_SAFE_REGION * p.y/100; - mMarginLeft = (100 - VIDEO_SAFE_REGION) * p.x / 200; - mMarginTop = (100 - VIDEO_SAFE_REGION) * p.y / 200; + final Rect bounds = getWindow().getWindowManager().getMaximumWindowMetrics() + .getBounds(); + mUsableWidth = VIDEO_SAFE_REGION * bounds.width() / 100; + mUsableHeight = VIDEO_SAFE_REGION * bounds.height() / 100; + mMarginLeft = (100 - VIDEO_SAFE_REGION) * bounds.width() / 200; + mMarginTop = (100 - VIDEO_SAFE_REGION) * bounds.height() / 200; - setContentView(mLayoutInflater.inflate(R.layout.keyguard_presentation, null)); + setContentView(LayoutInflater.from(getContext()) + .inflate(R.layout.keyguard_presentation, null)); // Logic to make the lock screen fullscreen getWindow().getDecorView().setSystemUiVisibility( diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardRootViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardRootViewController.java index 5c125fcc95cb..4e375c2d1227 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardRootViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardRootViewController.java @@ -19,7 +19,7 @@ package com.android.keyguard; import android.view.ViewGroup; import com.android.keyguard.dagger.KeyguardBouncerScope; -import com.android.keyguard.dagger.RootView; +import com.android.systemui.dagger.qualifiers.RootView; import com.android.systemui.statusbar.phone.KeyguardBouncer; import com.android.systemui.util.ViewController; diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java index a479bca56c2a..a9c06edf46cc 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java @@ -33,9 +33,11 @@ import android.text.TextUtils; import android.text.TextUtils.TruncateAt; import android.util.AttributeSet; import android.util.TypedValue; +import android.view.Gravity; import android.view.View; import android.view.animation.Animation; import android.widget.LinearLayout; +import android.widget.RelativeLayout; import android.widget.TextView; import androidx.slice.SliceItem; @@ -55,8 +57,10 @@ import com.android.systemui.util.wakelock.KeepAwakeAnimationListener; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; /** * View visible under the clock on the lock screen and AoD. @@ -86,6 +90,8 @@ public class KeyguardSliceView extends LinearLayout { private float mRowWithHeaderTextSize; private View.OnClickListener mOnClickListener; + private int mLockScreenMode = KeyguardUpdateMonitor.LOCK_SCREEN_MODE_NORMAL; + public KeyguardSliceView(Context context, AttributeSet attrs) { super(context, attrs); @@ -142,6 +148,40 @@ public class KeyguardSliceView extends LinearLayout { } } + /** + * Updates the lockscreen mode which may change the layout of the keyguard slice view. + */ + public void updateLockScreenMode(int mode) { + mLockScreenMode = mode; + if (mLockScreenMode == KeyguardUpdateMonitor.LOCK_SCREEN_MODE_LAYOUT_1) { + // add top padding to better align with top of clock + final int topPadding = (int) TypedValue.applyDimension( + TypedValue.COMPLEX_UNIT_DIP, + 20, + getResources().getDisplayMetrics()); + mTitle.setPaddingRelative(0, topPadding, 0, 0); + mTitle.setGravity(Gravity.START); + setGravity(Gravity.START); + RelativeLayout.LayoutParams lp = (RelativeLayout.LayoutParams) getLayoutParams(); + lp.removeRule(RelativeLayout.CENTER_HORIZONTAL); + setLayoutParams(lp); + } else { + final int horizontalPaddingDpValue = (int) TypedValue.applyDimension( + TypedValue.COMPLEX_UNIT_DIP, + 44, + getResources().getDisplayMetrics() + ); + mTitle.setPaddingRelative(horizontalPaddingDpValue, 0, horizontalPaddingDpValue, 0); + mTitle.setGravity(Gravity.CENTER_HORIZONTAL); + setGravity(Gravity.CENTER_HORIZONTAL); + RelativeLayout.LayoutParams lp = (RelativeLayout.LayoutParams) getLayoutParams(); + lp.addRule(RelativeLayout.CENTER_HORIZONTAL); + setLayoutParams(lp); + } + mRow.setLockscreenMode(mode); + requestLayout(); + } + Map<View, PendingIntent> showSlice(RowContent header, List<SliceContent> subItems) { Trace.beginSection("KeyguardSliceView#showSlice"); mHasHeader = header != null; @@ -166,6 +206,8 @@ public class KeyguardSliceView extends LinearLayout { final int startIndex = mHasHeader ? 1 : 0; // First item is header; skip it mRow.setVisibility(subItemsCount > 0 ? VISIBLE : GONE); LinearLayout.LayoutParams layoutParams = (LayoutParams) mRow.getLayoutParams(); + layoutParams.gravity = mLockScreenMode != KeyguardUpdateMonitor.LOCK_SCREEN_MODE_NORMAL + ? Gravity.START : Gravity.CENTER; layoutParams.topMargin = mHasHeader ? mRowWithHeaderPadding : mRowPadding; mRow.setLayoutParams(layoutParams); @@ -282,6 +324,7 @@ public class KeyguardSliceView extends LinearLayout { pw.println(" mTextColor: " + Integer.toHexString(mTextColor)); pw.println(" mDarkAmount: " + mDarkAmount); pw.println(" mHasHeader: " + mHasHeader); + pw.println(" mLockScreenMode: " + mLockScreenMode); } @Override @@ -291,6 +334,8 @@ public class KeyguardSliceView extends LinearLayout { } public static class Row extends LinearLayout { + private Set<KeyguardSliceTextView> mKeyguardSliceTextViewSet = new HashSet(); + private int mLockScreenModeRow = KeyguardUpdateMonitor.LOCK_SCREEN_MODE_NORMAL; /** * This view is visible in AOD, which means that the device will sleep if we @@ -361,12 +406,18 @@ public class KeyguardSliceView extends LinearLayout { protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int width = MeasureSpec.getSize(widthMeasureSpec); int childCount = getChildCount(); + for (int i = 0; i < childCount; i++) { View child = getChildAt(i); if (child instanceof KeyguardSliceTextView) { - ((KeyguardSliceTextView) child).setMaxWidth(width / 3); + if (mLockScreenModeRow == KeyguardUpdateMonitor.LOCK_SCREEN_MODE_LAYOUT_1) { + ((KeyguardSliceTextView) child).setMaxWidth(Integer.MAX_VALUE); + } else { + ((KeyguardSliceTextView) child).setMaxWidth(width / 3); + } } } + super.onMeasure(widthMeasureSpec, heightMeasureSpec); } @@ -384,6 +435,42 @@ public class KeyguardSliceView extends LinearLayout { public boolean hasOverlappingRendering() { return false; } + + @Override + public void addView(View view, int index) { + super.addView(view, index); + + if (view instanceof KeyguardSliceTextView) { + ((KeyguardSliceTextView) view).setLockScreenMode(mLockScreenModeRow); + mKeyguardSliceTextViewSet.add((KeyguardSliceTextView) view); + } + } + + @Override + public void removeView(View view) { + super.removeView(view); + if (view instanceof KeyguardSliceTextView) { + mKeyguardSliceTextViewSet.remove((KeyguardSliceTextView) view); + } + } + + /** + * Updates the lockscreen mode which may change the layout of this view. + */ + public void setLockscreenMode(int mode) { + mLockScreenModeRow = mode; + if (mLockScreenModeRow == KeyguardUpdateMonitor.LOCK_SCREEN_MODE_LAYOUT_1) { + setOrientation(LinearLayout.VERTICAL); + setGravity(Gravity.START); + } else { + setOrientation(LinearLayout.HORIZONTAL); + setGravity(Gravity.CENTER); + } + + for (KeyguardSliceTextView textView : mKeyguardSliceTextViewSet) { + textView.setLockScreenMode(mLockScreenModeRow); + } + } } /** @@ -392,6 +479,7 @@ public class KeyguardSliceView extends LinearLayout { @VisibleForTesting static class KeyguardSliceTextView extends TextView implements ConfigurationController.ConfigurationListener { + private int mLockScreenMode = KeyguardUpdateMonitor.LOCK_SCREEN_MODE_NORMAL; @StyleRes private static int sStyleId = R.style.TextAppearance_Keyguard_Secondary; @@ -432,9 +520,16 @@ public class KeyguardSliceView extends LinearLayout { private void updatePadding() { boolean hasText = !TextUtils.isEmpty(getText()); - int horizontalPadding = (int) getContext().getResources() + int padding = (int) getContext().getResources() .getDimension(R.dimen.widget_horizontal_padding) / 2; - setPadding(horizontalPadding, 0, horizontalPadding * (hasText ? 1 : -1), 0); + if (mLockScreenMode == KeyguardUpdateMonitor.LOCK_SCREEN_MODE_LAYOUT_1) { + // orientation is vertical, so add padding to top & bottom + setPadding(0, padding, 0, padding * (hasText ? 1 : -1)); + } else { + // oreintation is horizontal, so add padding to left & right + setPadding(padding, 0, padding * (hasText ? 1 : -1), 0); + } + setCompoundDrawablePadding((int) mContext.getResources() .getDimension(R.dimen.widget_icon_padding)); } @@ -461,5 +556,18 @@ public class KeyguardSliceView extends LinearLayout { } } } + + /** + * Updates the lockscreen mode which may change the layout of this view. + */ + public void setLockScreenMode(int mode) { + mLockScreenMode = mode; + if (mLockScreenMode == KeyguardUpdateMonitor.LOCK_SCREEN_MODE_LAYOUT_1) { + setGravity(Gravity.START); + } else { + setGravity(Gravity.CENTER); + } + updatePadding(); + } } } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceViewController.java index 2470b958a85f..8b55b06e5e5e 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceViewController.java @@ -44,6 +44,7 @@ import com.android.systemui.keyguard.KeyguardSliceProvider; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.tuner.TunerService; +import com.android.systemui.util.ViewController; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -55,11 +56,10 @@ import javax.inject.Inject; /** Controller for a {@link KeyguardSliceView}. */ @KeyguardStatusViewScope -public class KeyguardSliceViewController implements Dumpable { +public class KeyguardSliceViewController extends ViewController<KeyguardSliceView> implements + Dumpable { private static final String TAG = "KeyguardSliceViewCtrl"; - private final KeyguardSliceView mView; - private final KeyguardStatusView mKeyguardStatusView; private final ActivityStarter mActivityStarter; private final ConfigurationController mConfigurationController; private final TunerService mTunerService; @@ -69,41 +69,7 @@ public class KeyguardSliceViewController implements Dumpable { private Uri mKeyguardSliceUri; private Slice mSlice; private Map<View, PendingIntent> mClickActions; - - private final View.OnAttachStateChangeListener mOnAttachStateChangeListener = - new View.OnAttachStateChangeListener() { - - @Override - public void onViewAttachedToWindow(View v) { - - Display display = mView.getDisplay(); - if (display != null) { - mDisplayId = display.getDisplayId(); - } - mTunerService.addTunable(mTunable, Settings.Secure.KEYGUARD_SLICE_URI); - // Make sure we always have the most current slice - if (mDisplayId == DEFAULT_DISPLAY && mLiveData != null) { - mLiveData.observeForever(mObserver); - } - mConfigurationController.addCallback(mConfigurationListener); - mDumpManager.registerDumpable( - TAG + "@" + Integer.toHexString( - KeyguardSliceViewController.this.hashCode()), - KeyguardSliceViewController.this); - } - - @Override - public void onViewDetachedFromWindow(View v) { - - // TODO(b/117344873) Remove below work around after this issue be fixed. - if (mDisplayId == DEFAULT_DISPLAY) { - mLiveData.removeObserver(mObserver); - } - mTunerService.removeTunable(mTunable); - mConfigurationController.removeCallback(mConfigurationListener); - mDumpManager.unregisterDumpable(TAG); - } - }; + private int mLockScreenMode = KeyguardUpdateMonitor.LOCK_SCREEN_MODE_NORMAL; TunerService.Tunable mTunable = (key, newValue) -> setupUri(newValue); @@ -134,27 +100,57 @@ public class KeyguardSliceViewController implements Dumpable { }; @Inject - public KeyguardSliceViewController(KeyguardSliceView keyguardSliceView, - KeyguardStatusView keyguardStatusView, ActivityStarter activityStarter, - ConfigurationController configurationController, TunerService tunerService, + public KeyguardSliceViewController( + KeyguardSliceView keyguardSliceView, + ActivityStarter activityStarter, + ConfigurationController configurationController, + TunerService tunerService, DumpManager dumpManager) { - mView = keyguardSliceView; - mKeyguardStatusView = keyguardStatusView; + super(keyguardSliceView); mActivityStarter = activityStarter; mConfigurationController = configurationController; mTunerService = tunerService; mDumpManager = dumpManager; } - /** Initialize the controller. */ - public void init() { - if (mView.isAttachedToWindow()) { - mOnAttachStateChangeListener.onViewAttachedToWindow(mView); + @Override + protected void onViewAttached() { + Display display = mView.getDisplay(); + if (display != null) { + mDisplayId = display.getDisplayId(); + } + mTunerService.addTunable(mTunable, Settings.Secure.KEYGUARD_SLICE_URI); + // Make sure we always have the most current slice + if (mDisplayId == DEFAULT_DISPLAY && mLiveData != null) { + mLiveData.observeForever(mObserver); } - mView.addOnAttachStateChangeListener(mOnAttachStateChangeListener); - mView.setOnClickListener(mOnClickListener); - // TODO: remove the line below. - mKeyguardStatusView.setKeyguardSliceViewController(this); + mConfigurationController.addCallback(mConfigurationListener); + mDumpManager.registerDumpable( + TAG + "@" + Integer.toHexString( + KeyguardSliceViewController.this.hashCode()), + KeyguardSliceViewController.this); + mView.updateLockScreenMode(mLockScreenMode); + } + + @Override + protected void onViewDetached() { + // TODO(b/117344873) Remove below work around after this issue be fixed. + if (mDisplayId == DEFAULT_DISPLAY) { + mLiveData.removeObserver(mObserver); + } + mTunerService.removeTunable(mTunable); + mConfigurationController.removeCallback(mConfigurationListener); + mDumpManager.unregisterDumpable( + TAG + "@" + Integer.toHexString( + KeyguardSliceViewController.this.hashCode())); + } + + /** + * Updates the lockscreen mode which may change the layout of the keyguard slice view. + */ + public void updateLockScreenMode(int mode) { + mLockScreenMode = mode; + mView.updateLockScreenMode(mLockScreenMode); } /** @@ -228,12 +224,10 @@ public class KeyguardSliceViewController implements Dumpable { Trace.endSection(); } - @Override public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) { pw.println(" mSlice: " + mSlice); pw.println(" mClickActions: " + mClickActions); - - mKeyguardStatusView.dump(fd, pw, args); + pw.println(" mLockScreenMode: " + mLockScreenMode); } } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java index 9ef2def04ec1..2036b3321bda 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java @@ -19,16 +19,13 @@ package com.android.keyguard; import android.app.ActivityManager; import android.app.IActivityManager; import android.content.Context; -import android.content.res.Resources; import android.graphics.Color; import android.os.Handler; import android.os.RemoteException; import android.os.UserHandle; import android.text.TextUtils; -import android.text.format.DateFormat; import android.util.AttributeSet; import android.util.Log; -import android.util.Slog; import android.util.TypedValue; import android.view.View; import android.widget.GridLayout; @@ -39,15 +36,18 @@ import androidx.core.graphics.ColorUtils; import com.android.internal.widget.LockPatternUtils; import com.android.systemui.Dependency; import com.android.systemui.R; -import com.android.systemui.statusbar.policy.ConfigurationController; import java.io.FileDescriptor; import java.io.PrintWriter; -import java.util.Locale; -import java.util.TimeZone; -public class KeyguardStatusView extends GridLayout implements - ConfigurationController.ConfigurationListener { +/** + * View consisting of: + * - keyguard clock + * - logout button (on certain managed devices) + * - owner information (if set) + * - notification icons (shown on AOD) + */ +public class KeyguardStatusView extends GridLayout { private static final boolean DEBUG = KeyguardConstants.DEBUG; private static final String TAG = "KeyguardStatusView"; private static final int MARQUEE_DELAY_MS = 2000; @@ -62,9 +62,7 @@ public class KeyguardStatusView extends GridLayout implements private View mNotificationIcons; private Runnable mPendingMarqueeStart; private Handler mHandler; - private KeyguardSliceViewController mKeyguardSliceViewController; - private boolean mPulsing; private float mDarkAmount = 0; private int mTextColor; @@ -76,56 +74,6 @@ public class KeyguardStatusView extends GridLayout implements private int mIconTopMarginWithHeader; private boolean mShowingHeader; - private KeyguardUpdateMonitorCallback mInfoCallback = new KeyguardUpdateMonitorCallback() { - - @Override - public void onLockScreenModeChanged(int mode) { - updateLockScreenMode(mode); - } - - @Override - public void onTimeChanged() { - refreshTime(); - } - - @Override - public void onTimeZoneChanged(TimeZone timeZone) { - updateTimeZone(timeZone); - } - - @Override - public void onKeyguardVisibilityChanged(boolean showing) { - if (showing) { - if (DEBUG) Slog.v(TAG, "refresh statusview showing:" + showing); - refreshTime(); - updateOwnerInfo(); - updateLogoutView(); - } - } - - @Override - public void onStartedWakingUp() { - setEnableMarquee(true); - } - - @Override - public void onFinishedGoingToSleep(int why) { - setEnableMarquee(false); - } - - @Override - public void onUserSwitchComplete(int userId) { - refreshFormat(); - updateOwnerInfo(); - updateLogoutView(); - } - - @Override - public void onLogoutEnabledChanged() { - updateLogoutView(); - } - }; - public KeyguardStatusView(Context context) { this(context, null, 0); } @@ -142,21 +90,7 @@ public class KeyguardStatusView extends GridLayout implements onDensityOrFontScaleChanged(); } - /** - * If we're presenting a custom clock of just the default one. - */ - public boolean hasCustomClock() { - return mClockView.hasCustomClock(); - } - - /** - * Set whether or not the lock screen is showing notifications. - */ - public void setHasVisibleNotifications(boolean hasVisibleNotifications) { - mClockView.setHasVisibleNotifications(hasVisibleNotifications); - } - - private void setEnableMarquee(boolean enabled) { + void setEnableMarquee(boolean enabled) { if (DEBUG) Log.v(TAG, "Schedule setEnableMarquee: " + (enabled ? "Enable" : "Disable")); if (enabled) { if (mPendingMarqueeStart == null) { @@ -203,7 +137,6 @@ public class KeyguardStatusView extends GridLayout implements boolean shouldMarquee = Dependency.get(KeyguardUpdateMonitor.class).isDeviceInteractive(); setEnableMarquee(shouldMarquee); - refreshFormat(); updateOwnerInfo(); updateLogoutView(); updateDark(); @@ -238,64 +171,14 @@ public class KeyguardStatusView extends GridLayout implements layoutOwnerInfo(); } - @Override - public void onDensityOrFontScaleChanged() { - if (mClockView != null) { - mClockView.setTextSize(TypedValue.COMPLEX_UNIT_PX, - getResources().getDimensionPixelSize(R.dimen.widget_big_font_size)); - } - if (mOwnerInfo != null) { - mOwnerInfo.setTextSize(TypedValue.COMPLEX_UNIT_PX, - getResources().getDimensionPixelSize(R.dimen.widget_label_font_size)); - } - loadBottomMargin(); - } - - public void dozeTimeTick() { - refreshTime(); - mKeyguardSliceViewController.refresh(); - } - - private void refreshTime() { - mClockView.refresh(); - } - - private void updateLockScreenMode(int mode) { - mClockView.updateLockScreenMode(mode); - } - - private void updateTimeZone(TimeZone timeZone) { - mClockView.onTimeZoneChanged(timeZone); - } - - private void refreshFormat() { - Patterns.update(mContext); - mClockView.setFormat12Hour(Patterns.clockView12); - mClockView.setFormat24Hour(Patterns.clockView24); - } - - public int getLogoutButtonHeight() { + int getLogoutButtonHeight() { if (mLogoutView == null) { return 0; } return mLogoutView.getVisibility() == VISIBLE ? mLogoutView.getHeight() : 0; } - public float getClockTextSize() { - return mClockView.getTextSize(); - } - - /** - * Returns the preferred Y position of the clock. - * - * @param totalHeight The height available to position the clock. - * @return Y position of clock. - */ - public int getClockPreferredY(int totalHeight) { - return mClockView.getPreferredY(totalHeight); - } - - private void updateLogoutView() { + void updateLogoutView() { if (mLogoutView == null) { return; } @@ -305,7 +188,16 @@ public class KeyguardStatusView extends GridLayout implements com.android.internal.R.string.global_action_logout)); } - private void updateOwnerInfo() { + void onDensityOrFontScaleChanged() { + if (mOwnerInfo != null) { + mOwnerInfo.setTextSize(TypedValue.COMPLEX_UNIT_PX, + getResources().getDimensionPixelSize( + com.android.systemui.R.dimen.widget_label_font_size)); + loadBottomMargin(); + } + } + + void updateOwnerInfo() { if (mOwnerInfo == null) return; String info = mLockPatternUtils.getDeviceOwnerInfo(); if (info == null) { @@ -320,30 +212,36 @@ public class KeyguardStatusView extends GridLayout implements updateDark(); } - @Override - protected void onAttachedToWindow() { - super.onAttachedToWindow(); - Dependency.get(KeyguardUpdateMonitor.class).registerCallback(mInfoCallback); - Dependency.get(ConfigurationController.class).addCallback(this); + void setDarkAmount(float darkAmount) { + if (mDarkAmount == darkAmount) { + return; + } + mDarkAmount = darkAmount; + mClockView.setDarkAmount(darkAmount); + updateDark(); } - @Override - protected void onDetachedFromWindow() { - super.onDetachedFromWindow(); - Dependency.get(KeyguardUpdateMonitor.class).removeCallback(mInfoCallback); - Dependency.get(ConfigurationController.class).removeCallback(this); - } + void updateDark() { + boolean dark = mDarkAmount == 1; + if (mLogoutView != null) { + mLogoutView.setAlpha(dark ? 0 : 1); + } - @Override - public void onLocaleListChanged() { - refreshFormat(); + if (mOwnerInfo != null) { + boolean hasText = !TextUtils.isEmpty(mOwnerInfo.getText()); + mOwnerInfo.setVisibility(hasText ? VISIBLE : GONE); + layoutOwnerInfo(); + } + + final int blendedTextColor = ColorUtils.blendARGB(mTextColor, Color.WHITE, mDarkAmount); + mKeyguardSlice.setDarkAmount(mDarkAmount); + mClockView.setTextColor(blendedTextColor); } public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { pw.println("KeyguardStatusView:"); pw.println(" mOwnerInfo: " + (mOwnerInfo == null ? "null" : mOwnerInfo.getVisibility() == VISIBLE)); - pw.println(" mPulsing: " + mPulsing); pw.println(" mDarkAmount: " + mDarkAmount); pw.println(" mTextColor: " + Integer.toHexString(mTextColor)); if (mLogoutView != null) { @@ -363,64 +261,6 @@ public class KeyguardStatusView extends GridLayout implements R.dimen.widget_vertical_padding_with_header); } - // DateFormat.getBestDateTimePattern is extremely expensive, and refresh is called often. - // This is an optimization to ensure we only recompute the patterns when the inputs change. - private static final class Patterns { - static String clockView12; - static String clockView24; - static String cacheKey; - - static void update(Context context) { - final Locale locale = Locale.getDefault(); - final Resources res = context.getResources(); - final String clockView12Skel = res.getString(R.string.clock_12hr_format); - final String clockView24Skel = res.getString(R.string.clock_24hr_format); - final String key = locale.toString() + clockView12Skel + clockView24Skel; - if (key.equals(cacheKey)) return; - - clockView12 = DateFormat.getBestDateTimePattern(locale, clockView12Skel); - // CLDR insists on adding an AM/PM indicator even though it wasn't in the skeleton - // format. The following code removes the AM/PM indicator if we didn't want it. - if (!clockView12Skel.contains("a")) { - clockView12 = clockView12.replaceAll("a", "").trim(); - } - - clockView24 = DateFormat.getBestDateTimePattern(locale, clockView24Skel); - - // Use fancy colon. - clockView24 = clockView24.replace(':', '\uee01'); - clockView12 = clockView12.replace(':', '\uee01'); - - cacheKey = key; - } - } - - public void setDarkAmount(float darkAmount) { - if (mDarkAmount == darkAmount) { - return; - } - mDarkAmount = darkAmount; - mClockView.setDarkAmount(darkAmount); - updateDark(); - } - - private void updateDark() { - boolean dark = mDarkAmount == 1; - if (mLogoutView != null) { - mLogoutView.setAlpha(dark ? 0 : 1); - } - - if (mOwnerInfo != null) { - boolean hasText = !TextUtils.isEmpty(mOwnerInfo.getText()); - mOwnerInfo.setVisibility(hasText ? VISIBLE : GONE); - layoutOwnerInfo(); - } - - final int blendedTextColor = ColorUtils.blendARGB(mTextColor, Color.WHITE, mDarkAmount); - mKeyguardSlice.setDarkAmount(mDarkAmount); - mClockView.setTextColor(blendedTextColor); - } - private void layoutOwnerInfo() { if (mOwnerInfo != null && mOwnerInfo.getVisibility() != GONE) { // Animate owner info during wake-up transition @@ -442,13 +282,6 @@ public class KeyguardStatusView extends GridLayout implements } } - public void setPulsing(boolean pulsing) { - if (mPulsing == pulsing) { - return; - } - mPulsing = pulsing; - } - private boolean shouldShowLogout() { return Dependency.get(KeyguardUpdateMonitor.class).isLogoutEnabled() && KeyguardUpdateMonitor.getCurrentUser() != UserHandle.USER_SYSTEM; @@ -463,9 +296,4 @@ public class KeyguardStatusView extends GridLayout implements Log.e(TAG, "Failed to logout user", re); } } - - // TODO: remove this method when a controller is available. - void setKeyguardSliceViewController(KeyguardSliceViewController keyguardSliceViewController) { - mKeyguardSliceViewController = keyguardSliceViewController; - } } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java new file mode 100644 index 000000000000..7705db4e3147 --- /dev/null +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java @@ -0,0 +1,330 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.keyguard; + +import static com.android.systemui.statusbar.StatusBarState.KEYGUARD; + +import android.util.Slog; +import android.view.View; + +import com.android.systemui.Interpolators; +import com.android.systemui.statusbar.StatusBarState; +import com.android.systemui.statusbar.notification.AnimatableProperty; +import com.android.systemui.statusbar.notification.PropertyAnimator; +import com.android.systemui.statusbar.notification.stack.AnimationProperties; +import com.android.systemui.statusbar.notification.stack.StackStateAnimator; +import com.android.systemui.statusbar.policy.ConfigurationController; +import com.android.systemui.statusbar.policy.KeyguardStateController; +import com.android.systemui.util.ViewController; + +import java.util.TimeZone; + +import javax.inject.Inject; + +/** + * Injectable controller for {@link KeyguardStatusView}. + */ +public class KeyguardStatusViewController extends ViewController<KeyguardStatusView> { + private static final boolean DEBUG = KeyguardConstants.DEBUG; + private static final String TAG = "KeyguardStatusViewController"; + + private static final AnimationProperties CLOCK_ANIMATION_PROPERTIES = + new AnimationProperties().setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD); + + private final KeyguardSliceViewController mKeyguardSliceViewController; + private final KeyguardClockSwitchController mKeyguardClockSwitchController; + private final KeyguardStateController mKeyguardStateController; + private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; + private final ConfigurationController mConfigurationController; + + private boolean mKeyguardStatusViewAnimating; + + @Inject + public KeyguardStatusViewController( + KeyguardStatusView keyguardStatusView, + KeyguardSliceViewController keyguardSliceViewController, + KeyguardClockSwitchController keyguardClockSwitchController, + KeyguardStateController keyguardStateController, + KeyguardUpdateMonitor keyguardUpdateMonitor, + ConfigurationController configurationController) { + super(keyguardStatusView); + mKeyguardSliceViewController = keyguardSliceViewController; + mKeyguardClockSwitchController = keyguardClockSwitchController; + mKeyguardStateController = keyguardStateController; + mKeyguardUpdateMonitor = keyguardUpdateMonitor; + mConfigurationController = configurationController; + } + + @Override + public void init() { + super.init(); + mKeyguardClockSwitchController.init(); + } + + @Override + protected void onViewAttached() { + mKeyguardUpdateMonitor.registerCallback(mInfoCallback); + mConfigurationController.addCallback(mConfigurationListener); + } + + @Override + protected void onViewDetached() { + mKeyguardUpdateMonitor.removeCallback(mInfoCallback); + mConfigurationController.removeCallback(mConfigurationListener); + } + + /** + * Updates views on doze time tick. + */ + public void dozeTimeTick() { + refreshTime(); + mKeyguardSliceViewController.refresh(); + } + + /** + * The amount we're in doze. + */ + public void setDarkAmount(float darkAmount) { + mView.setDarkAmount(darkAmount); + } + + /** + * Set whether or not the lock screen is showing notifications. + */ + public void setHasVisibleNotifications(boolean hasVisibleNotifications) { + mKeyguardClockSwitchController.setHasVisibleNotifications(hasVisibleNotifications); + } + + /** + * If we're presenting a custom clock of just the default one. + */ + public boolean hasCustomClock() { + return mKeyguardClockSwitchController.hasCustomClock(); + } + + /** + * Get the height of the logout button. + */ + public int getLogoutButtonHeight() { + return mView.getLogoutButtonHeight(); + } + + /** + * Set keyguard status view alpha. + */ + public void setAlpha(float alpha) { + if (!mKeyguardStatusViewAnimating) { + mView.setAlpha(alpha); + } + } + + /** + * Set pivot x. + */ + public void setPivotX(float pivot) { + mView.setPivotX(pivot); + } + + /** + * Set pivot y. + */ + public void setPivotY(float pivot) { + mView.setPivotY(pivot); + } + + /** + * Get the clock text size. + */ + public float getClockTextSize() { + return mKeyguardClockSwitchController.getClockTextSize(); + } + + /** + * Returns the preferred Y position of the clock. + * + * @param totalHeight The height available to position the clock. + * @return Y position of clock. + */ + public int getClockPreferredY(int totalHeight) { + return mKeyguardClockSwitchController.getClockPreferredY(totalHeight); + } + + /** + * Get the height of the keyguard status view. + */ + public int getHeight() { + return mView.getHeight(); + } + + /** + * Set whether the view accessibility importance mode. + */ + public void setStatusAccessibilityImportance(int mode) { + mView.setImportantForAccessibility(mode); + } + + /** + * Update position of the view with an optional animation + */ + public void updatePosition(int clockTranslationX, int clockTranslationY, + boolean animateClock) { + PropertyAnimator.setProperty(mView, AnimatableProperty.X, + clockTranslationX, CLOCK_ANIMATION_PROPERTIES, animateClock); + PropertyAnimator.setProperty(mView, AnimatableProperty.Y, + clockTranslationY, CLOCK_ANIMATION_PROPERTIES, animateClock); + } + + /** + * Set the visibility of the keyguard status view based on some new state. + */ + public void setKeyguardStatusViewVisibility( + int statusBarState, + boolean keyguardFadingAway, + boolean goingToFullShade, + int oldStatusBarState) { + mView.animate().cancel(); + mKeyguardStatusViewAnimating = false; + if ((!keyguardFadingAway && oldStatusBarState == KEYGUARD + && statusBarState != KEYGUARD) || goingToFullShade) { + mKeyguardStatusViewAnimating = true; + mView.animate() + .alpha(0f) + .setStartDelay(0) + .setDuration(160) + .setInterpolator(Interpolators.ALPHA_OUT) + .withEndAction( + mAnimateKeyguardStatusViewGoneEndRunnable); + if (keyguardFadingAway) { + mView.animate() + .setStartDelay(mKeyguardStateController.getKeyguardFadingAwayDelay()) + .setDuration(mKeyguardStateController.getShortenedFadingAwayDuration()) + .start(); + } + } else if (oldStatusBarState == StatusBarState.SHADE_LOCKED && statusBarState == KEYGUARD) { + mView.setVisibility(View.VISIBLE); + mKeyguardStatusViewAnimating = true; + mView.setAlpha(0f); + mView.animate() + .alpha(1f) + .setStartDelay(0) + .setDuration(320) + .setInterpolator(Interpolators.ALPHA_IN) + .withEndAction(mAnimateKeyguardStatusViewVisibleEndRunnable); + } else if (statusBarState == KEYGUARD) { + if (keyguardFadingAway) { + mKeyguardStatusViewAnimating = true; + mView.animate() + .alpha(0) + .translationYBy(-getHeight() * 0.05f) + .setInterpolator(Interpolators.FAST_OUT_LINEAR_IN) + .setDuration(125) + .setStartDelay(0) + .withEndAction(mAnimateKeyguardStatusViewInvisibleEndRunnable) + .start(); + } else { + mView.setVisibility(View.VISIBLE); + mView.setAlpha(1f); + } + } else { + mView.setVisibility(View.GONE); + mView.setAlpha(1f); + } + } + + private void refreshTime() { + mKeyguardClockSwitchController.refresh(); + } + + private final ConfigurationController.ConfigurationListener mConfigurationListener = + new ConfigurationController.ConfigurationListener() { + @Override + public void onLocaleListChanged() { + refreshTime(); + } + + @Override + public void onDensityOrFontScaleChanged() { + mKeyguardClockSwitchController.onDensityOrFontScaleChanged(); + mView.onDensityOrFontScaleChanged(); + } + }; + + private KeyguardUpdateMonitorCallback mInfoCallback = new KeyguardUpdateMonitorCallback() { + @Override + public void onLockScreenModeChanged(int mode) { + mKeyguardClockSwitchController.updateLockScreenMode(mode); + mKeyguardSliceViewController.updateLockScreenMode(mode); + } + + @Override + public void onTimeChanged() { + refreshTime(); + } + + @Override + public void onTimeZoneChanged(TimeZone timeZone) { + mKeyguardClockSwitchController.updateTimeZone(timeZone); + } + + @Override + public void onKeyguardVisibilityChanged(boolean showing) { + if (showing) { + if (DEBUG) Slog.v(TAG, "refresh statusview showing:" + showing); + refreshTime(); + mView.updateOwnerInfo(); + mView.updateLogoutView(); + } + } + + @Override + public void onStartedWakingUp() { + mView.setEnableMarquee(true); + } + + @Override + public void onFinishedGoingToSleep(int why) { + mView.setEnableMarquee(false); + } + + @Override + public void onUserSwitchComplete(int userId) { + mKeyguardClockSwitchController.refreshFormat(); + mView.updateOwnerInfo(); + mView.updateLogoutView(); + } + + @Override + public void onLogoutEnabledChanged() { + mView.updateLogoutView(); + } + }; + + private final Runnable mAnimateKeyguardStatusViewInvisibleEndRunnable = () -> { + mKeyguardStatusViewAnimating = false; + mView.setVisibility(View.INVISIBLE); + }; + + + private final Runnable mAnimateKeyguardStatusViewGoneEndRunnable = () -> { + mKeyguardStatusViewAnimating = false; + mView.setVisibility(View.GONE); + }; + + private final Runnable mAnimateKeyguardStatusViewVisibleEndRunnable = () -> { + mKeyguardStatusViewAnimating = false; + }; +} diff --git a/packages/SystemUI/src/com/android/keyguard/TimeBasedColorsClockController.java b/packages/SystemUI/src/com/android/keyguard/TimeBasedColorsClockController.java new file mode 100644 index 000000000000..4f1963e5b534 --- /dev/null +++ b/packages/SystemUI/src/com/android/keyguard/TimeBasedColorsClockController.java @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.keyguard; + +import com.android.systemui.util.ViewController; + +import java.util.Calendar; +import java.util.GregorianCalendar; + +/** + * Changes the color of the text clock based on the time of day. + */ +public class TimeBasedColorsClockController extends ViewController<GradientTextClock> { + private final int[] mGradientColors = new int[3]; + private final float[] mPositions = new float[3]; + + public TimeBasedColorsClockController(GradientTextClock view) { + super(view); + } + + @Override + protected void onViewAttached() { + refreshTime(System.currentTimeMillis()); + } + + @Override + protected void onViewDetached() { + + } + + /** + * Updates the time for this view. Also updates any color changes. + */ + public void refreshTime(long timeInMillis) { + Calendar now = new GregorianCalendar(); + now.setTimeInMillis(timeInMillis); + updateColors(now); + updatePositions(now); + mView.refreshTime(); + } + + private int getTimeIndex(Calendar now) { + int hour = now.get(Calendar.HOUR_OF_DAY); // 0 - 23 + if (hour < mTimes[0]) { + return mTimes.length - 1; + } + + for (int i = 1; i < mTimes.length; i++) { + if (hour < mTimes[i]) { + return i - 1; + } + } + + return mTimes.length - 1; + } + + private void updateColors(Calendar now) { + final int index = getTimeIndex(now); + for (int i = 0; i < mGradientColors.length; i++) { + mGradientColors[i] = COLORS[index][i]; + } + mView.setGradientColors(mGradientColors); + } + + private void updatePositions(Calendar now) { + final int index = getTimeIndex(now); + + final Calendar startTime = new GregorianCalendar(); + startTime.setTimeInMillis(now.getTimeInMillis()); + startTime.set(Calendar.HOUR_OF_DAY, mTimes[index]); + if (startTime.getTimeInMillis() > now.getTimeInMillis()) { + // start should be earlier than 'now' + startTime.add(Calendar.DATE, -1); + } + + final Calendar endTime = new GregorianCalendar(); + endTime.setTimeInMillis(now.getTimeInMillis()); + if (index == mTimes.length - 1) { + endTime.set(Calendar.HOUR_OF_DAY, mTimes[0]); + endTime.add(Calendar.DATE, 1); // end time is tomorrow + } else { + endTime.set(Calendar.HOUR_OF_DAY, mTimes[index + 1]); + } + + long totalTimeInThisColorGradient = endTime.getTimeInMillis() - startTime.getTimeInMillis(); + long timeIntoThisColorGradient = now.getTimeInMillis() - startTime.getTimeInMillis(); + float percentageWithinGradient = + (float) timeIntoThisColorGradient / (float) totalTimeInThisColorGradient; + + for (int i = 0; i < mPositions.length; i++) { + // currently hard-coded .3 movement of gradient + mPositions[i] = POSITIONS[index][i] - (.3f * percentageWithinGradient); + } + mView.setColorPositions(mPositions); + } + + private static final int[] SUNRISE = new int[] {0xFF6F75AA, 0xFFAFF0FF, 0xFFFFDEBF}; + private static final int[] DAY = new int[] {0xFF9BD8FB, 0xFFD7F5FF, 0xFFFFF278}; + private static final int[] NIGHT = new int[] {0xFF333D5E, 0xFFC5A1D6, 0xFF907359}; + + private static final float[] SUNRISE_START_POSITIONS = new float[] {.3f, .5f, .8f}; + private static final float[] DAY_START_POSITIONS = new float[] {.4f, .8f, 1f}; + private static final float[] NIGHT_START_POSITIONS = new float[] {.25f, .5f, .8f}; + + // TODO (b/170228350): use TwilightManager to set sunrise/sunset times + private final int mSunriseTime = 6; // 6am + private final int mDaytime = 9; // 9 am + private final int mNightTime = 19; // 7pm + + private int[] mTimes = new int[] { + mSunriseTime, + mDaytime, + mNightTime + }; + private static final int[][] COLORS = new int[][] { + SUNRISE, + DAY, + NIGHT + }; + private static final float[][] POSITIONS = new float[][] { + SUNRISE_START_POSITIONS, + DAY_START_POSITIONS, + NIGHT_START_POSITIONS + }; +} diff --git a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardBouncerModule.java b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardBouncerModule.java index 881108858b51..4fad9a916d0d 100644 --- a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardBouncerModule.java +++ b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardBouncerModule.java @@ -24,6 +24,7 @@ import com.android.keyguard.KeyguardMessageArea; import com.android.keyguard.KeyguardSecurityContainer; import com.android.keyguard.KeyguardSecurityViewFlipper; import com.android.systemui.R; +import com.android.systemui.dagger.qualifiers.RootView; import com.android.systemui.statusbar.phone.KeyguardBouncer; import dagger.Module; diff --git a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusViewComponent.java b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusViewComponent.java index 21ccff707d34..1b6476ce74df 100644 --- a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusViewComponent.java +++ b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusViewComponent.java @@ -18,6 +18,7 @@ package com.android.keyguard.dagger; import com.android.keyguard.KeyguardClockSwitchController; import com.android.keyguard.KeyguardStatusView; +import com.android.keyguard.KeyguardStatusViewController; import dagger.BindsInstance; import dagger.Subcomponent; @@ -36,4 +37,7 @@ public interface KeyguardStatusViewComponent { /** Builds a {@link com.android.keyguard.KeyguardClockSwitchController}. */ KeyguardClockSwitchController getKeyguardClockSwitchController(); + + /** Builds a {@link com.android.keyguard.KeyguardStatusViewController}. */ + KeyguardStatusViewController getKeyguardStatusViewController(); } diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java index 9f28e0936d7f..8ac6edb9dfa2 100644 --- a/packages/SystemUI/src/com/android/systemui/Dependency.java +++ b/packages/SystemUI/src/com/android/systemui/Dependency.java @@ -119,13 +119,11 @@ import com.android.systemui.statusbar.policy.ZenModeController; import com.android.systemui.tracing.ProtoTracer; import com.android.systemui.tuner.TunablePadding.TunablePaddingService; import com.android.systemui.tuner.TunerService; +import com.android.systemui.util.DeviceConfigProxy; import com.android.systemui.util.leak.GarbageMonitor; import com.android.systemui.util.leak.LeakDetector; import com.android.systemui.util.leak.LeakReporter; import com.android.systemui.util.sensors.AsyncSensorManager; -import com.android.wm.shell.common.DisplayController; -import com.android.wm.shell.common.DisplayImeController; -import com.android.wm.shell.common.SystemWindows; import java.util.concurrent.Executor; import java.util.function.Consumer; @@ -340,12 +338,10 @@ public class Dependency { @Inject Lazy<CommandQueue> mCommandQueue; @Inject Lazy<Recents> mRecents; @Inject Lazy<StatusBar> mStatusBar; - @Inject Lazy<DisplayController> mDisplayController; - @Inject Lazy<SystemWindows> mSystemWindows; - @Inject Lazy<DisplayImeController> mDisplayImeController; @Inject Lazy<RecordingController> mRecordingController; @Inject Lazy<ProtoTracer> mProtoTracer; @Inject Lazy<MediaOutputDialogFactory> mMediaOutputDialogFactory; + @Inject Lazy<DeviceConfigProxy> mDeviceConfigProxy; @Inject public Dependency() { @@ -530,10 +526,8 @@ public class Dependency { mProviders.put(CommandQueue.class, mCommandQueue::get); mProviders.put(Recents.class, mRecents::get); mProviders.put(StatusBar.class, mStatusBar::get); - mProviders.put(DisplayController.class, mDisplayController::get); - mProviders.put(SystemWindows.class, mSystemWindows::get); - mProviders.put(DisplayImeController.class, mDisplayImeController::get); mProviders.put(ProtoTracer.class, mProtoTracer::get); + mProviders.put(DeviceConfigProxy.class, mDeviceConfigProxy::get); // TODO(b/118592525): to support multi-display , we start to add something which is // per-display, while others may be global. I think it's time to add diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java index c4a305ec41e2..8147f6619cd1 100644 --- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java +++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java @@ -317,10 +317,9 @@ public class ScreenDecorations extends SystemUI implements Tunable { updateColorInversion(value); } }; - - mColorInversionSetting.setListening(true); - mColorInversionSetting.onChange(false); } + mColorInversionSetting.setListening(true); + mColorInversionSetting.onChange(false); IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_USER_SWITCHED); @@ -582,6 +581,10 @@ public class ScreenDecorations extends SystemUI implements Tunable { @Override protected void onConfigurationChanged(Configuration newConfig) { + if (DEBUG_DISABLE_SCREEN_DECORATIONS) { + Log.i(TAG, "ScreenDecorations is disabled"); + return; + } mHandler.post(() -> { int oldRotation = mRotation; mPendingRotationChange = false; @@ -636,8 +639,8 @@ public class ScreenDecorations extends SystemUI implements Tunable { com.android.internal.R.dimen.rounded_corner_radius_bottom); final boolean changed = mRoundedDefault.x != newRoundedDefault - || mRoundedDefaultTop.x != newRoundedDefault - || mRoundedDefaultBottom.x != newRoundedDefault; + || mRoundedDefaultTop.x != newRoundedDefaultTop + || mRoundedDefaultBottom.x != newRoundedDefaultBottom; if (changed) { // If config_roundedCornerMultipleRadius set as true, ScreenDecorations respect the @@ -766,6 +769,10 @@ public class ScreenDecorations extends SystemUI implements Tunable { @Override public void onTuningChanged(String key, String newValue) { + if (DEBUG_DISABLE_SCREEN_DECORATIONS) { + Log.i(TAG, "ScreenDecorations is disabled"); + return; + } mHandler.post(() -> { if (mOverlays == null) return; if (SIZE.equals(key)) { diff --git a/packages/SystemUI/src/com/android/systemui/SlicePermissionActivity.java b/packages/SystemUI/src/com/android/systemui/SlicePermissionActivity.java index 2365f128532e..47adffc216a5 100644 --- a/packages/SystemUI/src/com/android/systemui/SlicePermissionActivity.java +++ b/packages/SystemUI/src/com/android/systemui/SlicePermissionActivity.java @@ -111,9 +111,7 @@ public class SlicePermissionActivity extends Activity implements OnClickListener final String providerPkg = getIntent().getStringExtra("provider_pkg"); if (providerPkg == null || mProviderPkg.equals(providerPkg)) return; final String callingPkg = getCallingPkg(); - EventLog.writeEvent(0x534e4554, "159145361", getUid(callingPkg), String.format( - "pkg %s (disguised as %s) attempted to request permission to show %s slices in %s", - callingPkg, providerPkg, mProviderPkg, mCallingPkg)); + EventLog.writeEvent(0x534e4554, "159145361", getUid(callingPkg)); } @Nullable diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java index 7dcec3d75367..b388a6eb5edd 100644 --- a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java +++ b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java @@ -19,15 +19,18 @@ package com.android.systemui; import android.app.ActivityThread; import android.app.Application; import android.content.BroadcastReceiver; +import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; import android.content.res.Configuration; import android.os.Process; import android.os.SystemProperties; import android.os.Trace; import android.os.UserHandle; +import android.provider.Settings; import android.util.Log; import android.util.TimingsTraceLog; @@ -35,6 +38,7 @@ import com.android.systemui.dagger.ContextComponentHelper; import com.android.systemui.dagger.GlobalRootComponent; import com.android.systemui.dagger.SysUIComponent; import com.android.systemui.dump.DumpManager; +import com.android.systemui.people.PeopleSpaceActivity; import com.android.systemui.util.NotificationChannels; import java.lang.reflect.Constructor; @@ -104,6 +108,19 @@ public class SystemUIApplication extends Application implements mServices[i].onBootCompleted(); } } + // If flag SHOW_PEOPLE_SPACE is true, enable People Space launcher icon. + try { + int showPeopleSpace = Settings.Global.getInt(context.getContentResolver(), + Settings.Global.SHOW_PEOPLE_SPACE); + context.getPackageManager().setComponentEnabledSetting( + new ComponentName(context, PeopleSpaceActivity.class), + showPeopleSpace == 1 + ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED + : PackageManager.COMPONENT_ENABLED_STATE_DISABLED, + PackageManager.DONT_KILL_APP); + } catch (Exception e) { + Log.w(TAG, "Error enabling People Space launch icon:", e); + } } }, bootCompletedFilter); diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java index 80253b424335..bdd0b55b27ea 100644 --- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java +++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java @@ -30,6 +30,7 @@ import com.android.systemui.dagger.WMComponent; import com.android.systemui.navigationbar.gestural.BackGestureTfClassifierProvider; import com.android.systemui.screenshot.ScreenshotNotificationSmartActionsProvider; +import java.util.Optional; import java.util.concurrent.ExecutionException; import java.util.concurrent.Executor; @@ -49,6 +50,11 @@ public class SystemUIFactory { } public static void createFromConfig(Context context) { + createFromConfig(context, false); + } + + @VisibleForTesting + public static void createFromConfig(Context context, boolean fromTest) { if (mFactory != null) { return; } @@ -62,7 +68,7 @@ public class SystemUIFactory { Class<?> cls = null; cls = context.getClassLoader().loadClass(clsName); mFactory = (SystemUIFactory) cls.newInstance(); - mFactory.init(context); + mFactory.init(context, fromTest); } catch (Throwable t) { Log.w(TAG, "Error creating SystemUIFactory component: " + clsName, t); throw new RuntimeException(t); @@ -76,16 +82,40 @@ public class SystemUIFactory { public SystemUIFactory() {} - private void init(Context context) throws ExecutionException, InterruptedException { + @VisibleForTesting + public void init(Context context, boolean fromTest) + throws ExecutionException, InterruptedException { + final boolean initializeComponents = !fromTest + && android.os.Process.myUserHandle().isSystem(); mRootComponent = buildGlobalRootComponent(context); // Stand up WMComponent mWMComponent = mRootComponent.getWMComponentBuilder().build(); + if (initializeComponents) { + // Only initialize when not starting from tests since this currently initializes some + // components that shouldn't be run in the test environment + mWMComponent.init(); + } // And finally, retrieve whatever SysUI needs from WMShell and build SysUI. - // TODO: StubAPIClass is just a placeholder. - mSysUIComponent = mRootComponent.getSysUIComponent() - .setStubAPIClass(mWMComponent.createStubAPIClass()) + SysUIComponent.Builder builder = mRootComponent.getSysUIComponent(); + if (initializeComponents) { + // Only initialize when not starting from tests since this currently initializes some + // components that shouldn't be run in the test environment + builder = builder.setPip(mWMComponent.getPip()) + .setSplitScreen(mWMComponent.getSplitScreen()) + .setOneHanded(mWMComponent.getOneHanded()); + } else { + builder = builder.setPip(Optional.ofNullable(null)) + .setSplitScreen(Optional.ofNullable(null)) + .setOneHanded(Optional.ofNullable(null)); + } + mSysUIComponent = builder + .setInputConsumerController(mWMComponent.getInputConsumerController()) + .setShellTaskOrganizer(mWMComponent.getShellTaskOrganizer()) .build(); + if (initializeComponents) { + mSysUIComponent.init(); + } // Every other part of our codebase currently relies on Dependency, so we // really need to ensure the Dependency gets initialized early on. @@ -93,6 +123,16 @@ public class SystemUIFactory { dependency.start(); } + /** + * Prepares the SysUIComponent builder before it is built. + * @param sysUIBuilder the builder provided by the root component's getSysUIComponent() method + * @param wm the built WMComponent from the root component's getWMComponent() method + */ + protected SysUIComponent.Builder prepareSysUIComponentBuilder( + SysUIComponent.Builder sysUIBuilder, WMComponent wm) { + return sysUIBuilder; + } + protected GlobalRootComponent buildGlobalRootComponent(Context context) { return DaggerGlobalRootComponent.builder() .context(context) diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java index 69a0d65a6963..c6e5f09af572 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java @@ -31,6 +31,7 @@ import android.view.MotionEvent; import android.view.View; import android.view.ViewConfiguration; import android.view.WindowManager; +import android.view.accessibility.AccessibilityManager; import android.view.accessibility.AccessibilityNodeInfo; import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction; import android.widget.ImageView; @@ -48,15 +49,16 @@ class MagnificationModeSwitch { @VisibleForTesting static final long FADING_ANIMATION_DURATION_MS = 300; - private static final int DEFAULT_FADE_OUT_ANIMATION_DELAY_MS = 3000; - // The button visible duration starting from the last showButton() called. - private int mVisibleDuration = DEFAULT_FADE_OUT_ANIMATION_DELAY_MS; + @VisibleForTesting + static final int DEFAULT_FADE_OUT_ANIMATION_DELAY_MS = 3000; + private int mUiTimeout; private final Runnable mFadeInAnimationTask; private final Runnable mFadeOutAnimationTask; @VisibleForTesting boolean mIsFadeOutAnimating = false; private final Context mContext; + private final AccessibilityManager mAccessibilityManager; private final WindowManager mWindowManager; private final ImageView mImageView; private final PointF mLastDown = new PointF(); @@ -74,6 +76,7 @@ class MagnificationModeSwitch { @VisibleForTesting MagnificationModeSwitch(Context context, @NonNull ImageView imageView) { mContext = context; + mAccessibilityManager = mContext.getSystemService(AccessibilityManager.class); mWindowManager = (WindowManager) mContext.getSystemService( Context.WINDOW_SERVICE); mParams = createLayoutParams(); @@ -202,6 +205,10 @@ class MagnificationModeSwitch { mWindowManager.addView(mImageView, mParams); mIsVisible = true; mImageView.postOnAnimation(mFadeInAnimationTask); + mUiTimeout = mAccessibilityManager.getRecommendedTimeoutMillis( + DEFAULT_FADE_OUT_ANIMATION_DELAY_MS, + AccessibilityManager.FLAG_CONTENT_ICONS + | AccessibilityManager.FLAG_CONTENT_CONTROLS); } if (mIsFadeOutAnimating) { mImageView.animate().cancel(); @@ -209,7 +216,7 @@ class MagnificationModeSwitch { } // Refresh the time slot of the fade-out task whenever this method is called. mImageView.removeCallbacks(mFadeOutAnimationTask); - mImageView.postOnAnimationDelayed(mFadeOutAnimationTask, mVisibleDuration); + mImageView.postOnAnimationDelayed(mFadeOutAnimationTask, mUiTimeout); } void onConfigurationChanged(int configDiff) { diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java index a705ec784c9a..e9e453b3d981 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java @@ -72,8 +72,7 @@ public class WindowMagnification extends SystemUI implements WindowMagnifierCall super(context); mHandler = mainHandler; mLastConfiguration = new Configuration(context.getResources().getConfiguration()); - mAccessibilityManager = (AccessibilityManager) mContext.getSystemService( - Context.ACCESSIBILITY_SERVICE); + mAccessibilityManager = mContext.getSystemService(AccessibilityManager.class); mCommandQueue = commandQueue; mModeSwitchesController = modeSwitchesController; final WindowMagnificationController controller = new WindowMagnificationController(mContext, diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java index c3474bb7ca57..340ca044a1ef 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java @@ -249,8 +249,8 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold if ((configDiff & ActivityInfo.CONFIG_DENSITY) != 0) { updateDimensions(); if (isWindowVisible()) { - mWm.removeView(mMirrorView); - createMirrorWindow(); + deleteWindowMagnification(); + enableWindowMagnification(Float.NaN, Float.NaN, Float.NaN); } } else if ((configDiff & ActivityInfo.CONFIG_ORIENTATION) != 0) { onRotate(); diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java index 57d8dc70cd88..1891daf59007 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java @@ -646,14 +646,14 @@ class Bubble implements BubbleViewProvider { } private int getDimenForPackageUser(Context context, int resId, String pkg, int userId) { - PackageManager pm = context.getPackageManager(); Resources r; if (pkg != null) { try { if (userId == UserHandle.USER_ALL) { userId = UserHandle.USER_SYSTEM; } - r = pm.getResourcesForApplicationAsUser(pkg, userId); + r = context.createContextAsUser(UserHandle.of(userId), /* flags */ 0) + .getPackageManager().getResourcesForApplication(pkg); return r.getDimensionPixelSize(resId); } catch (PackageManager.NameNotFoundException ex) { // Uninstalled, don't care diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java index 3f94b00d3c60..0c3dc8222a34 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java @@ -177,6 +177,7 @@ public class BubbleController implements Bubbles, ConfigurationController.Config private ScrimView mBubbleScrim; @Nullable private BubbleStackView mStackView; private BubbleIconFactory mBubbleIconFactory; + private BubblePositioner mBubblePositioner; /** * The relative position of the stack when we removed it and nulled it out. If the stack is @@ -244,7 +245,7 @@ public class BubbleController implements Bubbles, ConfigurationController.Config private boolean mInflateSynchronously; - private MultiWindowTaskListener mTaskListener; + private ShellTaskOrganizer mTaskOrganizer; // TODO (b/145659174): allow for multiple callbacks to support the "shadow" new notif pipeline private final List<NotifCallback> mCallbacks = new ArrayList<>(); @@ -387,7 +388,7 @@ public class BubbleController implements Bubbles, ConfigurationController.Config dumpManager, floatingContentCoordinator, new BubbleDataRepository(context, launcherApps), sysUiState, notificationManager, statusBarService, windowManager, windowManagerShellWrapper, launcherApps, logger, - mainHandler, organizer); + mainHandler, organizer, new BubblePositioner(context, windowManager)); } /** @@ -419,7 +420,8 @@ public class BubbleController implements Bubbles, ConfigurationController.Config LauncherApps launcherApps, BubbleLogger bubbleLogger, Handler mainHandler, - ShellTaskOrganizer organizer) { + ShellTaskOrganizer organizer, + BubblePositioner positioner) { dumpManager.registerDumpable(TAG, this); mContext = context; mShadeController = shadeController; @@ -529,7 +531,8 @@ public class BubbleController implements Bubbles, ConfigurationController.Config }); mBubbleIconFactory = new BubbleIconFactory(context); - mTaskListener = new MultiWindowTaskListener(mMainHandler, organizer); + mTaskOrganizer = organizer; + mBubblePositioner = positioner; launcherApps.registerCallback(new LauncherApps.Callback() { @Override @@ -805,8 +808,13 @@ public class BubbleController implements Bubbles, ConfigurationController.Config } @Override - public MultiWindowTaskListener getTaskManager() { - return mTaskListener; + public ShellTaskOrganizer getTaskOrganizer() { + return mTaskOrganizer; + } + + @Override + public BubblePositioner getPositioner() { + return mBubblePositioner; } /** @@ -818,9 +826,10 @@ public class BubbleController implements Bubbles, ConfigurationController.Config mStackView = new BubbleStackView( mContext, mBubbleData, mSurfaceSynchronizer, mFloatingContentCoordinator, this::onAllBubblesAnimatedOut, this::onImeVisibilityChanged, - this::hideCurrentInputMethod, this::onBubbleExpandChanged); + this::hideCurrentInputMethod, this::onBubbleExpandChanged, mBubblePositioner); mStackView.setStackStartPosition(mPositionFromRemovedStack); mStackView.addView(mBubbleScrim); + mStackView.onOrientationChanged(); if (mExpandListener != null) { mStackView.setExpandListener(mExpandListener); } @@ -978,10 +987,14 @@ public class BubbleController implements Bubbles, ConfigurationController.Config @Override public void onConfigChanged(Configuration newConfig) { + if (mBubblePositioner != null) { + // This doesn't trigger any changes, always update it + mBubblePositioner.update(newConfig.orientation); + } if (mStackView != null && newConfig != null) { if (newConfig.orientation != mOrientation) { mOrientation = newConfig.orientation; - mStackView.onOrientationChanged(newConfig.orientation); + mStackView.onOrientationChanged(); } if (newConfig.densityDpi != mDensityDpi) { mDensityDpi = newConfig.densityDpi; diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java index 98a2257d2daa..bc060209132b 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java @@ -38,7 +38,6 @@ import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.Color; import android.graphics.Outline; -import android.graphics.Point; import android.graphics.Rect; import android.graphics.drawable.ShapeDrawable; import android.os.Bundle; @@ -48,7 +47,6 @@ import android.view.SurfaceControl; import android.view.View; import android.view.ViewGroup; import android.view.ViewOutlineProvider; -import android.view.WindowManager; import android.view.accessibility.AccessibilityNodeInfo; import android.widget.FrameLayout; import android.widget.LinearLayout; @@ -60,6 +58,7 @@ import com.android.systemui.Dependency; import com.android.systemui.R; import com.android.systemui.recents.TriangleShape; import com.android.systemui.statusbar.AlphaOptimizedButton; +import com.android.wm.shell.common.HandlerExecutor; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -83,24 +82,26 @@ public class BubbleExpandedView extends LinearLayout { private boolean mImeVisible; private boolean mNeedsNewHeight; - private Point mDisplaySize; private int mMinHeight; private int mOverflowHeight; private int mSettingsIconHeight; private int mPointerWidth; private int mPointerHeight; - private ShapeDrawable mPointerDrawable; + private ShapeDrawable mCurrentPointer; + private ShapeDrawable mTopPointer; + private ShapeDrawable mLeftPointer; + private ShapeDrawable mRightPointer; private int mExpandedViewPadding; private float mCornerRadius = 0f; @Nullable private Bubble mBubble; private PendingIntent mPendingIntent; - + // TODO(b/170891664): Don't use a flag, set the BubbleOverflow object instead private boolean mIsOverflow; private Bubbles mBubbles = Dependency.get(Bubbles.class); - private WindowManager mWindowManager; private BubbleStackView mStackView; + private BubblePositioner mPositioner; /** * Container for the ActivityView that has a solid, round-rect background that shows if the @@ -136,6 +137,7 @@ public class BubbleExpandedView extends LinearLayout { } try { if (!mIsOverflow && mBubble.hasMetadataShortcutId()) { + options.setApplyActivityFlagsForBubbles(true); mTaskView.startShortcutActivity(mBubble.getShortcutInfo(), options, null /* sourceBounds */); } else { @@ -224,17 +226,6 @@ public class BubbleExpandedView extends LinearLayout { updateDimensions(); } - void updateDimensions() { - mDisplaySize = new Point(); - mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE); - // Get the real size -- this includes screen decorations (notches, statusbar, navbar). - mWindowManager.getDefaultDisplay().getRealSize(mDisplaySize); - Resources res = getResources(); - mMinHeight = res.getDimensionPixelSize(R.dimen.bubble_expanded_default_height); - mOverflowHeight = res.getDimensionPixelSize(R.dimen.bubble_overflow_height); - mPointerMargin = res.getDimensionPixelSize(R.dimen.bubble_pointer_margin); - } - @SuppressLint("ClickableViewAccessibility") @Override protected void onFinishInflate() { @@ -245,15 +236,24 @@ public class BubbleExpandedView extends LinearLayout { mPointerWidth = res.getDimensionPixelSize(R.dimen.bubble_pointer_width); mPointerHeight = res.getDimensionPixelSize(R.dimen.bubble_pointer_height); - mPointerDrawable = new ShapeDrawable(TriangleShape.create( + mTopPointer = new ShapeDrawable(TriangleShape.create( mPointerWidth, mPointerHeight, true /* pointUp */)); + mLeftPointer = new ShapeDrawable(TriangleShape.createHorizontal( + mPointerWidth, mPointerHeight, true /* pointLeft */)); + mRightPointer = new ShapeDrawable(TriangleShape.createHorizontal( + mPointerWidth, mPointerHeight, false /* pointLeft */)); + + mCurrentPointer = mTopPointer; mPointerView.setVisibility(INVISIBLE); mSettingsIconHeight = getContext().getResources().getDimensionPixelSize( R.dimen.bubble_manage_button_height); mSettingsIcon = findViewById(R.id.settings_button); - mTaskView = new TaskView(mContext, mBubbles.getTaskManager()); + mPositioner = mBubbles.getPositioner(); + + mTaskView = new TaskView(mContext, mBubbles.getTaskOrganizer(), + new HandlerExecutor(getHandler())); // Set ActivityView's alpha value as zero, since there is no view content to be shown. setContentVisibility(false); @@ -282,8 +282,7 @@ public class BubbleExpandedView extends LinearLayout { applyThemeAttrs(); mExpandedViewPadding = res.getDimensionPixelSize(R.dimen.bubble_expanded_view_padding); - setPadding(mExpandedViewPadding, mExpandedViewPadding, mExpandedViewPadding, - mExpandedViewPadding); + setClipToPadding(false); setOnTouchListener((view, motionEvent) -> { if (mTaskView == null) { return false; @@ -311,6 +310,52 @@ public class BubbleExpandedView extends LinearLayout { setLayoutDirection(LAYOUT_DIRECTION_LOCALE); } + void updateDimensions() { + Resources res = getResources(); + mMinHeight = res.getDimensionPixelSize(R.dimen.bubble_expanded_default_height); + mOverflowHeight = res.getDimensionPixelSize(R.dimen.bubble_overflow_height); + mPointerMargin = res.getDimensionPixelSize(R.dimen.bubble_pointer_margin); + } + + void applyThemeAttrs() { + final TypedArray ta = mContext.obtainStyledAttributes(new int[] { + android.R.attr.dialogCornerRadius, + android.R.attr.colorBackgroundFloating}); + mCornerRadius = ta.getDimensionPixelSize(0, 0); + mExpandedViewContainer.setBackgroundColor(ta.getColor(1, Color.WHITE)); + ta.recycle(); + + if (mTaskView != null && ScreenDecorationsUtils.supportsRoundedCornersOnWindows( + mContext.getResources())) { + mTaskView.setCornerRadius(mCornerRadius); + } + updatePointerView(); + } + + private void updatePointerView() { + final int mode = + getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK; + switch (mode) { + case Configuration.UI_MODE_NIGHT_NO: + mCurrentPointer.setTint(getResources().getColor(R.color.bubbles_light)); + break; + case Configuration.UI_MODE_NIGHT_YES: + mCurrentPointer.setTint(getResources().getColor(R.color.bubbles_dark)); + break; + } + LayoutParams lp = (LayoutParams) mPointerView.getLayoutParams(); + if (mCurrentPointer == mLeftPointer || mCurrentPointer == mRightPointer) { + lp.width = mPointerHeight; + lp.height = mPointerWidth; + } else { + lp.width = mPointerWidth; + lp.height = mPointerHeight; + } + mPointerView.setLayoutParams(lp); + mPointerView.setBackground(mCurrentPointer); + } + + private String getBubbleKey() { return mBubble != null ? mBubble.getKey() : "null"; } @@ -371,32 +416,6 @@ public class BubbleExpandedView extends LinearLayout { } } - void applyThemeAttrs() { - final TypedArray ta = mContext.obtainStyledAttributes(new int[] { - android.R.attr.dialogCornerRadius, - android.R.attr.colorBackgroundFloating}); - mCornerRadius = ta.getDimensionPixelSize(0, 0); - mExpandedViewContainer.setBackgroundColor(ta.getColor(1, Color.WHITE)); - ta.recycle(); - - if (mTaskView != null && ScreenDecorationsUtils.supportsRoundedCornersOnWindows( - mContext.getResources())) { - mTaskView.setCornerRadius(mCornerRadius); - } - - final int mode = - getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK; - switch (mode) { - case Configuration.UI_MODE_NIGHT_NO: - mPointerDrawable.setTint(getResources().getColor(R.color.bubbles_light)); - break; - case Configuration.UI_MODE_NIGHT_YES: - mPointerDrawable.setTint(getResources().getColor(R.color.bubbles_dark)); - break; - } - mPointerView.setBackground(mPointerDrawable); - } - @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); @@ -522,12 +541,12 @@ public class BubbleExpandedView extends LinearLayout { } if (mBubble != null || mIsOverflow) { - float desiredHeight = mOverflowHeight; - if (!mIsOverflow) { - desiredHeight = Math.max(mBubble.getDesiredHeight(mContext), mMinHeight); - } + float desiredHeight = mIsOverflow + ? mOverflowHeight + : mBubble.getDesiredHeight(mContext); + desiredHeight = Math.max(desiredHeight, mMinHeight); float height = Math.min(desiredHeight, getMaxExpandedHeight()); - height = Math.max(height, mIsOverflow ? mOverflowHeight : mMinHeight); + height = Math.max(height, mMinHeight); FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mTaskView.getLayoutParams(); mNeedsNewHeight = lp.height != height; if (!mImeVisible) { @@ -546,21 +565,17 @@ public class BubbleExpandedView extends LinearLayout { } private int getMaxExpandedHeight() { - mWindowManager.getDefaultDisplay().getRealSize(mDisplaySize); int expandedContainerY = mExpandedViewContainerLocation != null - ? mExpandedViewContainerLocation[1] + // Remove top insets back here because availableRect.height would account for that + ? mExpandedViewContainerLocation[1] - mPositioner.getInsets().top : 0; - int bottomInset = getRootWindowInsets() != null - ? getRootWindowInsets().getStableInsetBottom() - : 0; - - return mDisplaySize.y + return mPositioner.getAvailableRect().height() - expandedContainerY - getPaddingTop() - getPaddingBottom() - mSettingsIconHeight - mPointerHeight - - mPointerMargin - bottomInset; + - mPointerMargin; } /** @@ -585,12 +600,25 @@ public class BubbleExpandedView extends LinearLayout { } /** - * Set the x position that the tip of the triangle should point to. + * Set the position that the tip of the triangle should point to. */ - public void setPointerPosition(float x) { - float halfPointerWidth = mPointerWidth / 2f; - float pointerLeft = x - halfPointerWidth - mExpandedViewPadding; - mPointerView.setTranslationX(pointerLeft); + public void setPointerPosition(float x, float y, boolean isLandscape, boolean onLeft) { + // Pointer gets drawn in the padding + int paddingLeft = (isLandscape && onLeft) ? mPointerHeight : 0; + int paddingRight = (isLandscape && !onLeft) ? mPointerHeight : 0; + int paddingTop = isLandscape ? 0 : mExpandedViewPadding; + setPadding(paddingLeft, paddingTop, paddingRight, 0); + + if (isLandscape) { + // TODO: why setY vs setTranslationY ? linearlayout? + mPointerView.setY(y - (mPointerWidth / 2f)); + mPointerView.setTranslationX(onLeft ? -mPointerHeight : x - mExpandedViewPadding); + } else { + mPointerView.setTranslationY(0f); + mPointerView.setTranslationX(x - mExpandedViewPadding - (mPointerWidth / 2f)); + } + mCurrentPointer = isLandscape ? onLeft ? mLeftPointer : mRightPointer : mTopPointer; + updatePointerView(); mPointerView.setVisibility(VISIBLE); } diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleFlyoutView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleFlyoutView.java index 69f7828ff8fc..d8b32500db85 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleFlyoutView.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleFlyoutView.java @@ -18,6 +18,8 @@ package com.android.systemui.bubbles; import static android.graphics.Paint.ANTI_ALIAS_FLAG; import static android.graphics.Paint.FILTER_BITMAP_FLAG; +import static com.android.systemui.Interpolators.ALPHA_IN; +import static com.android.systemui.Interpolators.ALPHA_OUT; import android.animation.ArgbEvaluator; import android.content.Context; @@ -56,6 +58,12 @@ public class BubbleFlyoutView extends FrameLayout { /** Max width of the flyout, in terms of percent of the screen width. */ private static final float FLYOUT_MAX_WIDTH_PERCENT = .6f; + /** Translation Y of fade animation. */ + private static final float FLYOUT_FADE_Y = 40f; + + private static final long FLYOUT_FADE_OUT_DURATION = 150L; + private static final long FLYOUT_FADE_IN_DURATION = 250L; + private final int mFlyoutPadding; private final int mFlyoutSpaceFromBubble; private final int mPointerSize; @@ -104,6 +112,9 @@ public class BubbleFlyoutView extends FrameLayout { /** The bounds of the flyout background, kept up to date as it transitions to the 'new' dot. */ private final RectF mBgRect = new RectF(); + /** The y position of the flyout, relative to the top of the screen. */ + private float mFlyoutY = 0f; + /** * Percent progress in the transition from flyout to 'new' dot. These two values are the inverse * of each other (if we're 40% transitioned to the dot, we're 60% flyout), but it makes the code @@ -221,18 +232,39 @@ public class BubbleFlyoutView extends FrameLayout { mSenderText.setTextSize(TypedValue.COMPLEX_UNIT_PX, newFontSize); } - /** Configures the flyout, collapsed into to dot form. */ - void setupFlyoutStartingAsDot( - Bubble.FlyoutMessage flyoutMessage, - PointF stackPos, - float parentWidth, - boolean arrowPointingLeft, - int dotColor, - @Nullable Runnable onLayoutComplete, - @Nullable Runnable onHide, - float[] dotCenter, - boolean hideDot) { + /* + * Fade animation for consecutive flyouts. + */ + void animateUpdate(Bubble.FlyoutMessage flyoutMessage, float parentWidth, float stackY) { + final Runnable afterFadeOut = () -> { + updateFlyoutMessage(flyoutMessage, parentWidth); + // Wait for TextViews to layout with updated height. + post(() -> { + mFlyoutY = stackY + (mBubbleSize - mFlyoutTextContainer.getHeight()) / 2f; + fade(true /* in */, () -> {} /* after */); + } /* after */ ); + }; + fade(false /* in */, afterFadeOut); + } + + /* + * Fade-out above or fade-in from below. + */ + private void fade(boolean in, Runnable afterFade) { + setAlpha(in ? 0f : 1f); + setTranslationY(in ? mFlyoutY + FLYOUT_FADE_Y : mFlyoutY); + animate() + .alpha(in ? 1f : 0f) + .setDuration(in ? FLYOUT_FADE_IN_DURATION : FLYOUT_FADE_OUT_DURATION) + .setInterpolator(in ? ALPHA_IN : ALPHA_OUT); + animate() + .translationY(in ? mFlyoutY : mFlyoutY - FLYOUT_FADE_Y) + .setDuration(in ? FLYOUT_FADE_IN_DURATION : FLYOUT_FADE_OUT_DURATION) + .setInterpolator(in ? ALPHA_IN : ALPHA_OUT) + .withEndAction(afterFade); + } + private void updateFlyoutMessage(Bubble.FlyoutMessage flyoutMessage, float parentWidth) { final Drawable senderAvatar = flyoutMessage.senderAvatar; if (senderAvatar != null && flyoutMessage.isGroupChat) { mSenderAvatar.setVisibility(VISIBLE); @@ -256,6 +288,27 @@ public class BubbleFlyoutView extends FrameLayout { mSenderText.setVisibility(GONE); } + // Set the flyout TextView's max width in terms of percent, and then subtract out the + // padding so that the entire flyout view will be the desired width (rather than the + // TextView being the desired width + extra padding). + mMessageText.setMaxWidth(maxTextViewWidth); + mMessageText.setText(flyoutMessage.message); + } + + /** Configures the flyout, collapsed into dot form. */ + void setupFlyoutStartingAsDot( + Bubble.FlyoutMessage flyoutMessage, + PointF stackPos, + float parentWidth, + boolean arrowPointingLeft, + int dotColor, + @Nullable Runnable onLayoutComplete, + @Nullable Runnable onHide, + float[] dotCenter, + boolean hideDot) { + + updateFlyoutMessage(flyoutMessage, parentWidth); + mArrowPointingLeft = arrowPointingLeft; mDotColor = dotColor; mOnHide = onHide; @@ -263,24 +316,12 @@ public class BubbleFlyoutView extends FrameLayout { setCollapsePercent(1f); - // Set the flyout TextView's max width in terms of percent, and then subtract out the - // padding so that the entire flyout view will be the desired width (rather than the - // TextView being the desired width + extra padding). - mMessageText.setMaxWidth(maxTextViewWidth); - mMessageText.setText(flyoutMessage.message); - - // Wait for the TextView to lay out so we know its line count. + // Wait for TextViews to layout with updated height. post(() -> { - float restingTranslationY; - // Multi line flyouts get top-aligned to the bubble. - if (mMessageText.getLineCount() > 1) { - restingTranslationY = stackPos.y + mBubbleIconTopPadding; - } else { - // Single line flyouts are vertically centered with respect to the bubble. - restingTranslationY = - stackPos.y + (mBubbleSize - mFlyoutTextContainer.getHeight()) / 2f; - } - setTranslationY(restingTranslationY); + // Flyout is vertically centered with respect to the bubble. + mFlyoutY = + stackPos.y + (mBubbleSize - mFlyoutTextContainer.getHeight()) / 2f; + setTranslationY(mFlyoutY); // Calculate the translation required to position the flyout next to the bubble stack, // with the desired padding. @@ -300,7 +341,7 @@ public class BubbleFlyoutView extends FrameLayout { final float dotPositionY = stackPos.y + mDotCenter[1] - adjustmentForScaleAway; final float distanceFromFlyoutLeftToDotCenterX = mRestingTranslationX - dotPositionX; - final float distanceFromLayoutTopToDotCenterY = restingTranslationY - dotPositionY; + final float distanceFromLayoutTopToDotCenterY = mFlyoutY - dotPositionY; mTranslationXWhenDot = -distanceFromFlyoutLeftToDotCenterX; mTranslationYWhenDot = -distanceFromLayoutTopToDotCenterY; diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubblePositioner.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubblePositioner.java new file mode 100644 index 000000000000..029caee6364f --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubblePositioner.java @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.bubbles; + +import android.content.Context; +import android.content.res.Configuration; +import android.graphics.Insets; +import android.graphics.Rect; +import android.view.WindowInsets; +import android.view.WindowManager; +import android.view.WindowMetrics; + +import androidx.annotation.VisibleForTesting; + +/** + * Keeps track of display size, configuration, and specific bubble sizes. One place for all + * placement and positioning calculations to refer to. + */ +public class BubblePositioner { + + private WindowManager mWindowManager; + private Rect mPositionRect; + private int mOrientation; + private Insets mInsets; + + public BubblePositioner(Context context, WindowManager windowManager) { + mWindowManager = windowManager; + update(Configuration.ORIENTATION_UNDEFINED); + } + + public void update(int orientation) { + WindowMetrics windowMetrics = mWindowManager.getCurrentWindowMetrics(); + mPositionRect = new Rect(windowMetrics.getBounds()); + WindowInsets metricInsets = windowMetrics.getWindowInsets(); + + Insets insets = metricInsets.getInsetsIgnoringVisibility(WindowInsets.Type.navigationBars() + | WindowInsets.Type.statusBars() + | WindowInsets.Type.displayCutout()); + update(orientation, insets, windowMetrics.getBounds()); + } + + @VisibleForTesting + public void update(int orientation, Insets insets, Rect bounds) { + mOrientation = orientation; + mInsets = insets; + + mPositionRect = new Rect(bounds); + mPositionRect.left += mInsets.left; + mPositionRect.top += mInsets.top; + mPositionRect.right -= mInsets.right; + mPositionRect.bottom -= mInsets.bottom; + } + + /** + * @return a rect of available screen space for displaying bubbles in the correct orientation, + * accounting for system bars and cutouts. + */ + public Rect getAvailableRect() { + return mPositionRect; + } + + /** + * @return the current orientation. + */ + public int getOrientation() { + return mOrientation; + } + + /** + * @return the relevant insets (status bar, nav bar, cutouts). + */ + public Insets getInsets() { + return mInsets; + } +} diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java index e2674de3a723..0714c5eb4fa4 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java @@ -35,9 +35,9 @@ import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.ColorMatrix; import android.graphics.ColorMatrixColorFilter; +import android.graphics.Insets; import android.graphics.Outline; import android.graphics.Paint; -import android.graphics.Point; import android.graphics.PointF; import android.graphics.Rect; import android.graphics.RectF; @@ -47,7 +47,6 @@ import android.os.Handler; import android.provider.Settings; import android.util.Log; import android.view.Choreographer; -import android.view.DisplayCutout; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.SurfaceControl; @@ -57,7 +56,6 @@ import android.view.ViewGroup; import android.view.ViewOutlineProvider; import android.view.ViewTreeObserver; import android.view.WindowInsets; -import android.view.WindowManager; import android.view.accessibility.AccessibilityNodeInfo; import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction; import android.view.animation.AccelerateDecelerateInterpolator; @@ -188,8 +186,6 @@ public class BubbleStackView extends FrameLayout } }; - private Point mDisplaySize; - private final BubbleData mBubbleData; private final ValueAnimator mDesaturateAndDarkenAnimator; @@ -245,8 +241,8 @@ public class BubbleStackView extends FrameLayout private int mBubblePaddingTop; private int mBubbleTouchPadding; private int mExpandedViewPadding; + private int mPointerHeight; private int mCornerRadius; - private int mStatusBarHeight; private int mImeOffset; @Nullable private BubbleViewProvider mExpandedBubble; private boolean mIsExpanded; @@ -721,13 +717,11 @@ public class BubbleStackView extends FrameLayout } }; - private DismissView mDismissView; - private int mOrientation = Configuration.ORIENTATION_UNDEFINED; - @Nullable private BubbleOverflow mBubbleOverflow; private StackEducationView mStackEduView; private ManageEducationView mManageEduView; + private DismissView mDismissView; private ViewGroup mManageMenu; private ImageView mManageSettingsIcon; @@ -735,6 +729,8 @@ public class BubbleStackView extends FrameLayout private boolean mShowingManage = false; private PhysicsAnimator.SpringConfig mManageSpringConfig = new PhysicsAnimator.SpringConfig( SpringForce.STIFFNESS_MEDIUM, SpringForce.DAMPING_RATIO_LOW_BOUNCY); + private BubblePositioner mPositioner; + @SuppressLint("ClickableViewAccessibility") public BubbleStackView(Context context, BubbleData data, @Nullable SurfaceSynchronizer synchronizer, @@ -742,7 +738,8 @@ public class BubbleStackView extends FrameLayout Runnable allBubblesAnimatedOutAction, Consumer<Boolean> onImeVisibilityChanged, Runnable hideCurrentInputMethodCallback, - Consumer<Boolean> onBubbleExpandChanged) { + Consumer<Boolean> onBubbleExpandChanged, + BubblePositioner positioner) { super(context); mBubbleData = data; @@ -753,15 +750,11 @@ public class BubbleStackView extends FrameLayout mBubbleElevation = res.getDimensionPixelSize(R.dimen.bubble_elevation); mBubblePaddingTop = res.getDimensionPixelSize(R.dimen.bubble_padding_top); mBubbleTouchPadding = res.getDimensionPixelSize(R.dimen.bubble_touch_padding); + mPointerHeight = res.getDimensionPixelSize(R.dimen.bubble_pointer_height); - mStatusBarHeight = - res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height); mImeOffset = res.getDimensionPixelSize(R.dimen.pip_ime_offset); - mDisplaySize = new Point(); - WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); - // We use the real size & subtract screen decorations / window insets ourselves when needed - wm.getDefaultDisplay().getRealSize(mDisplaySize); + mPositioner = positioner; mExpandedViewPadding = res.getDimensionPixelSize(R.dimen.bubble_expanded_view_padding); int elevation = res.getDimensionPixelSize(R.dimen.bubble_elevation); @@ -778,11 +771,10 @@ public class BubbleStackView extends FrameLayout }; mStackAnimationController = new StackAnimationController( - floatingContentCoordinator, this::getBubbleCount, onBubbleAnimatedOut); + floatingContentCoordinator, this::getBubbleCount, onBubbleAnimatedOut, mPositioner); mExpandedAnimationController = new ExpandedAnimationController( - mDisplaySize, mExpandedViewPadding, res.getConfiguration().orientation, - onBubbleAnimatedOut); + mPositioner, mExpandedViewPadding, onBubbleAnimatedOut); mSurfaceSynchronizer = synchronizer != null ? synchronizer : DEFAULT_SURFACE_SYNCHRONIZER; // Force LTR by default since most of the Bubbles UI is positioned manually by the user, or @@ -817,10 +809,10 @@ public class BubbleStackView extends FrameLayout mAnimatingOutSurfaceContainer.addView(mAnimatingOutSurfaceView); mAnimatingOutSurfaceContainer.setPadding( - mExpandedViewPadding, - mExpandedViewPadding, - mExpandedViewPadding, - mExpandedViewPadding); + mExpandedViewContainer.getPaddingLeft(), + mExpandedViewContainer.getPaddingTop(), + mExpandedViewContainer.getPaddingRight(), + mExpandedViewContainer.getPaddingBottom()); setUpManageMenu(); @@ -871,29 +863,16 @@ public class BubbleStackView extends FrameLayout mOrientationChangedListener = (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> { - mExpandedAnimationController.updateResources(mOrientation, mDisplaySize); - mStackAnimationController.updateResources(mOrientation); + onDisplaySizeChanged(); + mExpandedAnimationController.updateResources(); + mStackAnimationController.updateResources(); mBubbleOverflow.updateResources(); - // Need to update the padding around the view - WindowInsets insets = getRootWindowInsets(); - int leftPadding = mExpandedViewPadding; - int rightPadding = mExpandedViewPadding; - if (insets != null) { - // Can't have the expanded view overlaying notches - int cutoutLeft = 0; - int cutoutRight = 0; - DisplayCutout cutout = insets.getDisplayCutout(); - if (cutout != null) { - cutoutLeft = cutout.getSafeInsetLeft(); - cutoutRight = cutout.getSafeInsetRight(); - } - // Or overlaying nav or status bar - leftPadding += Math.max(cutoutLeft, insets.getStableInsetLeft()); - rightPadding += Math.max(cutoutRight, insets.getStableInsetRight()); + if (mRelativeStackPositionBeforeRotation != null) { + mStackAnimationController.setStackPosition( + mRelativeStackPositionBeforeRotation); + mRelativeStackPositionBeforeRotation = null; } - mExpandedViewContainer.setPadding(leftPadding, mExpandedViewPadding, - rightPadding, mExpandedViewPadding); if (mIsExpanded) { // Re-draw bubble row and pointer for new orientation. @@ -903,15 +882,10 @@ public class BubbleStackView extends FrameLayout mExpandedAnimationController.expandFromStack(() -> { afterExpandedViewAnimation(); } /* after */); - mExpandedViewContainer.setTranslationX(0); + mExpandedViewContainer.setTranslationX(0f); mExpandedViewContainer.setTranslationY(getExpandedViewY()); mExpandedViewContainer.setAlpha(1f); } - if (mRelativeStackPositionBeforeRotation != null) { - mStackAnimationController.setStackPosition( - mRelativeStackPositionBeforeRotation); - mRelativeStackPositionBeforeRotation = null; - } removeOnLayoutChangeListener(mOrientationChangedListener); }; @@ -1178,26 +1152,16 @@ public class BubbleStackView extends FrameLayout } /** Respond to the phone being rotated by repositioning the stack and hiding any flyouts. */ - public void onOrientationChanged(int orientation) { - mOrientation = orientation; - - // Display size is based on the rotation device was in when requested, we should update it - // We use the real size & subtract screen decorations / window insets ourselves when needed - WindowManager wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE); - wm.getDefaultDisplay().getRealSize(mDisplaySize); - - // Some resources change depending on orientation + public void onOrientationChanged() { Resources res = getContext().getResources(); - mStatusBarHeight = res.getDimensionPixelSize( - com.android.internal.R.dimen.status_bar_height); mBubblePaddingTop = res.getDimensionPixelSize(R.dimen.bubble_padding_top); mRelativeStackPositionBeforeRotation = mStackAnimationController.getRelativeStackPosition(); - addOnLayoutChangeListener(mOrientationChangedListener); - hideFlyoutImmediate(); - mManageMenu.setVisibility(View.INVISIBLE); mShowingManage = false; + + addOnLayoutChangeListener(mOrientationChangedListener); + hideFlyoutImmediate(); } /** Tells the views with locale-dependent layout direction to resolve the new direction. */ @@ -1217,11 +1181,7 @@ public class BubbleStackView extends FrameLayout public void onDisplaySizeChanged() { updateOverflow(); - WindowManager wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE); - wm.getDefaultDisplay().getRealSize(mDisplaySize); Resources res = getContext().getResources(); - mStatusBarHeight = res.getDimensionPixelSize( - com.android.internal.R.dimen.status_bar_height); mBubblePaddingTop = res.getDimensionPixelSize(R.dimen.bubble_padding_top); mBubbleSize = getResources().getDimensionPixelSize(R.dimen.individual_bubble_size); for (Bubble b : mBubbleData.getBubbles()) { @@ -1231,8 +1191,8 @@ public class BubbleStackView extends FrameLayout } b.getIconView().setLayoutParams(new LayoutParams(mBubbleSize, mBubbleSize)); } - mExpandedAnimationController.updateResources(mOrientation, mDisplaySize); - mStackAnimationController.updateResources(mOrientation); + mExpandedAnimationController.updateResources(); + mStackAnimationController.updateResources(); mDismissView.updateResources(); mMagneticTarget.setMagneticFieldRadiusPx(mBubbleSize * 2); } @@ -1682,7 +1642,8 @@ public class BubbleStackView extends FrameLayout private void animateExpansion() { cancelDelayedExpandCollapseSwitchAnimations(); - + final boolean isLandscape = + mPositioner.getOrientation() == Configuration.ORIENTATION_LANDSCAPE; mIsExpanded = true; if (mStackEduView != null) { mStackEduView.hide(true /* fromExpansion */); @@ -1698,20 +1659,21 @@ public class BubbleStackView extends FrameLayout } } /* after */); - mExpandedViewContainer.setTranslationX(0); + mExpandedViewContainer.setTranslationX(0f); mExpandedViewContainer.setTranslationY(getExpandedViewY()); mExpandedViewContainer.setAlpha(1f); // X-value of the bubble we're expanding, once it's settled in its row. - final float bubbleWillBeAtX = - mExpandedAnimationController.getBubbleLeft( + final float bubbleWillBeAt = + mExpandedAnimationController.getBubbleXOrYForOrientation( mBubbleData.getBubbles().indexOf(mExpandedBubble)); // How far horizontally the bubble will be animating. We'll wait a bit longer for bubbles // that are animating farther, so that the expanded view doesn't move as much. - final float horizontalDistanceAnimated = - Math.abs(bubbleWillBeAtX - - mStackAnimationController.getStackPosition().x); + final float relevantStackPosition = isLandscape + ? mStackAnimationController.getStackPosition().y + : mStackAnimationController.getStackPosition().x; + final float distanceAnimated = Math.abs(bubbleWillBeAt - relevantStackPosition); // Wait for the path animation target to reach its end, and add a small amount of extra time // if the bubble is moving a lot horizontally. @@ -1721,13 +1683,26 @@ public class BubbleStackView extends FrameLayout if (getWidth() > 0) { startDelay = (long) (ExpandedAnimationController.EXPAND_COLLAPSE_TARGET_ANIM_DURATION - + (horizontalDistanceAnimated / getWidth()) * 30); + + (distanceAnimated / getWidth()) * 30); } // Set the pivot point for the scale, so the expanded view animates out from the bubble. - mExpandedViewContainerMatrix.setScale( - 0f, 0f, - bubbleWillBeAtX + mBubbleSize / 2f, getExpandedViewY()); + if (isLandscape) { + float pivotX; + float pivotY = bubbleWillBeAt + mBubbleSize / 2f; + if (mStackOnLeftOrWillBe) { + pivotX = mPositioner.getAvailableRect().left + mBubbleSize + mExpandedViewPadding; + } else { + pivotX = mPositioner.getAvailableRect().right - mBubbleSize - mExpandedViewPadding; + } + mExpandedViewContainerMatrix.setScale( + 0f, 0f, + pivotX, pivotY); + } else { + mExpandedViewContainerMatrix.setScale( + 0f, 0f, + bubbleWillBeAt + mBubbleSize / 2f, getExpandedViewY()); + } mExpandedViewContainer.setAnimationMatrix(mExpandedViewContainerMatrix); if (mExpandedBubble != null && mExpandedBubble.getExpandedView() != null) { @@ -1747,9 +1722,11 @@ public class BubbleStackView extends FrameLayout if (mExpandedBubble == null || mExpandedBubble.getIconView() == null) { return; } + float translation = isLandscape + ? mExpandedBubble.getIconView().getTranslationY() + : mExpandedBubble.getIconView().getTranslationX(); mExpandedViewContainerMatrix.postTranslate( - mExpandedBubble.getIconView().getTranslationX() - - bubbleWillBeAtX, + translation - bubbleWillBeAt, 0); mExpandedViewContainer.setAnimationMatrix( mExpandedViewContainerMatrix); @@ -1797,16 +1774,29 @@ public class BubbleStackView extends FrameLayout // We want to visually collapse into this bubble during the animation. final View expandingFromBubble = mExpandedBubble.getIconView(); - // X-value the bubble is animating from (back into the stack). - final float expandingFromBubbleAtX = - mExpandedAnimationController.getBubbleLeft( + // Value the bubble is animating from (back into the stack). + final float expandingFromBubbleAt = + mExpandedAnimationController.getBubbleXOrYForOrientation( mBubbleData.getBubbles().indexOf(mExpandedBubble)); - - // Set the pivot point. - mExpandedViewContainerMatrix.setScale( - 1f, 1f, - expandingFromBubbleAtX + mBubbleSize / 2f, - getExpandedViewY()); + final boolean isLandscape = + mPositioner.getOrientation() == Configuration.ORIENTATION_LANDSCAPE; + if (isLandscape) { + float pivotX; + float pivotY = expandingFromBubbleAt + mBubbleSize / 2f; + if (mStackOnLeftOrWillBe) { + pivotX = mPositioner.getAvailableRect().left + mBubbleSize + mExpandedViewPadding; + } else { + pivotX = mPositioner.getAvailableRect().right - mBubbleSize - mExpandedViewPadding; + } + mExpandedViewContainerMatrix.setScale( + 1f, 1f, + pivotX, pivotY); + } else { + mExpandedViewContainerMatrix.setScale( + 1f, 1f, + expandingFromBubbleAt + mBubbleSize / 2f, + getExpandedViewY()); + } PhysicsAnimator.getInstance(mExpandedViewContainerMatrix).cancel(); PhysicsAnimator.getInstance(mExpandedViewContainerMatrix) @@ -1815,9 +1805,15 @@ public class BubbleStackView extends FrameLayout .addUpdateListener((target, values) -> { if (expandingFromBubble != null) { // Follow the bubble as it translates! - mExpandedViewContainerMatrix.postTranslate( - expandingFromBubble.getTranslationX() - - expandingFromBubbleAtX, 0f); + if (isLandscape) { + mExpandedViewContainerMatrix.postTranslate( + 0f, expandingFromBubble.getTranslationY() + - expandingFromBubbleAt); + } else { + mExpandedViewContainerMatrix.postTranslate( + expandingFromBubble.getTranslationX() + - expandingFromBubbleAt, 0f); + } } mExpandedViewContainer.setAnimationMatrix(mExpandedViewContainerMatrix); @@ -1861,26 +1857,55 @@ public class BubbleStackView extends FrameLayout // The surface contains a screenshot of the animating out bubble, so we just need to animate // it out (and then release the GraphicBuffer). PhysicsAnimator.getInstance(mAnimatingOutSurfaceContainer).cancel(); - PhysicsAnimator.getInstance(mAnimatingOutSurfaceContainer) + PhysicsAnimator animator = PhysicsAnimator.getInstance(mAnimatingOutSurfaceContainer) .spring(DynamicAnimation.SCALE_X, 0f, mScaleOutSpringConfig) .spring(DynamicAnimation.SCALE_Y, 0f, mScaleOutSpringConfig) - .spring(DynamicAnimation.TRANSLATION_Y, - mAnimatingOutSurfaceContainer.getTranslationY() - mBubbleSize * 2, - mTranslateSpringConfig) - .withEndActions(this::releaseAnimatingOutBubbleBuffer) - .start(); + .withEndActions(this::releaseAnimatingOutBubbleBuffer); + + if (mPositioner.getOrientation() == Configuration.ORIENTATION_LANDSCAPE) { + float translationX = mStackAnimationController.isStackOnLeftSide() + ? mAnimatingOutSurfaceContainer.getTranslationX() + mBubbleSize * 2 + : mAnimatingOutSurfaceContainer.getTranslationX(); + animator.spring(DynamicAnimation.TRANSLATION_X, + translationX, + mTranslateSpringConfig) + .start(); + } else { + animator.spring(DynamicAnimation.TRANSLATION_Y, + mAnimatingOutSurfaceContainer.getTranslationY() - mBubbleSize * 2, + mTranslateSpringConfig) + .start(); + } boolean isOverflow = mExpandedBubble != null && mExpandedBubble.getKey().equals(BubbleOverflow.KEY); - float expandingFromBubbleDestinationX = - mExpandedAnimationController.getBubbleLeft(isOverflow ? getBubbleCount() + float expandingFromBubbleDestination = + mExpandedAnimationController.getBubbleXOrYForOrientation(isOverflow + ? getBubbleCount() : mBubbleData.getBubbles().indexOf(mExpandedBubble)); mExpandedViewContainer.setAlpha(1f); mExpandedViewContainer.setVisibility(View.VISIBLE); - mExpandedViewContainerMatrix.setScale( - 0f, 0f, expandingFromBubbleDestinationX + mBubbleSize / 2f, getExpandedViewY()); + if (mPositioner.getOrientation() == Configuration.ORIENTATION_LANDSCAPE) { + float pivotX; + float pivotY = expandingFromBubbleDestination + mBubbleSize / 2f; + if (mStackOnLeftOrWillBe) { + pivotX = mPositioner.getAvailableRect().left + mBubbleSize + mExpandedViewPadding; + } else { + pivotX = mPositioner.getAvailableRect().right - mBubbleSize - mExpandedViewPadding; + + } + mExpandedViewContainerMatrix.setScale( + 0f, 0f, + pivotX, pivotY); + } else { + mExpandedViewContainerMatrix.setScale( + 0f, 0f, + expandingFromBubbleDestination + mBubbleSize / 2f, + getExpandedViewY()); + } + mExpandedViewContainer.setAnimationMatrix(mExpandedViewContainerMatrix); mDelayedAnimationHandler.postDelayed(() -> { @@ -2168,14 +2193,15 @@ public class BubbleStackView extends FrameLayout * Calculates the y position of the expanded view when it is expanded. */ float getExpandedViewY() { - return getStatusBarHeight() + mBubbleSize + mBubblePaddingTop; + final int top = mPositioner.getAvailableRect().top; + if (mPositioner.getOrientation() == Configuration.ORIENTATION_LANDSCAPE) { + return top + mExpandedViewPadding; + } else { + return top + mBubbleSize + mBubblePaddingTop; + } } - /** - * Animates in the flyout for the given bubble, if available, and then hides it after some time. - */ - @VisibleForTesting - void animateInFlyoutForBubble(Bubble bubble) { + private boolean shouldShowFlyout(Bubble bubble) { Bubble.FlyoutMessage flyoutMessage = bubble.getFlyoutMessage(); final BadgedImageView bubbleView = bubble.getIconView(); if (flyoutMessage == null @@ -2187,11 +2213,22 @@ public class BubbleStackView extends FrameLayout || mIsGestureInProgress || mBubbleToExpandAfterFlyoutCollapse != null || bubbleView == null) { - if (bubbleView != null) { + if (bubbleView != null && mFlyout.getVisibility() != VISIBLE) { bubbleView.removeDotSuppressionFlag(BadgedImageView.SuppressionFlag.FLYOUT_VISIBLE); } // Skip the message if none exists, we're expanded or animating expansion, or we're // about to expand a bubble from the previous tapped flyout, or if bubble view is null. + return false; + } + return true; + } + + /** + * Animates in the flyout for the given bubble, if available, and then hides it after some time. + */ + @VisibleForTesting + void animateInFlyoutForBubble(Bubble bubble) { + if (!shouldShowFlyout(bubble)) { return; } @@ -2209,25 +2246,23 @@ public class BubbleStackView extends FrameLayout } // Stop suppressing the dot now that the flyout has morphed into the dot. - bubbleView.removeDotSuppressionFlag( - BadgedImageView.SuppressionFlag.FLYOUT_VISIBLE); - - mFlyout.setVisibility(INVISIBLE); - + if (bubble.getIconView() != null) { + bubble.getIconView().removeDotSuppressionFlag( + BadgedImageView.SuppressionFlag.FLYOUT_VISIBLE); + } // Hide the stack after a delay, if needed. updateTemporarilyInvisibleAnimation(false /* hideImmediately */); }; - mFlyout.setVisibility(INVISIBLE); // Suppress the dot when we are animating the flyout. - bubbleView.addDotSuppressionFlag( + bubble.getIconView().addDotSuppressionFlag( BadgedImageView.SuppressionFlag.FLYOUT_VISIBLE); // Start flyout expansion. Post in case layout isn't complete and getWidth returns 0. post(() -> { // An auto-expanding bubble could have been posted during the time it takes to // layout. - if (isExpanded()) { + if (isExpanded() || bubble.getIconView() == null) { return; } final Runnable expandFlyoutAfterDelay = () -> { @@ -2244,18 +2279,21 @@ public class BubbleStackView extends FrameLayout mFlyout.postDelayed(mAnimateInFlyout, 200); }; - if (bubble.getIconView() == null) { - return; - } - mFlyout.setupFlyoutStartingAsDot(flyoutMessage, - mStackAnimationController.getStackPosition(), getWidth(), - mStackAnimationController.isStackOnLeftSide(), - bubble.getIconView().getDotColor() /* dotColor */, - expandFlyoutAfterDelay /* onLayoutComplete */, - mAfterFlyoutHidden, - bubble.getIconView().getDotCenter(), - !bubble.showDot()); + if (mFlyout.getVisibility() == View.VISIBLE) { + mFlyout.animateUpdate(bubble.getFlyoutMessage(), getWidth(), + mStackAnimationController.getStackPosition().y); + } else { + mFlyout.setVisibility(INVISIBLE); + mFlyout.setupFlyoutStartingAsDot(bubble.getFlyoutMessage(), + mStackAnimationController.getStackPosition(), getWidth(), + mStackAnimationController.isStackOnLeftSide(), + bubble.getIconView().getDotColor() /* dotColor */, + expandFlyoutAfterDelay /* onLayoutComplete */, + mAfterFlyoutHidden, + bubble.getIconView().getDotCenter(), + !bubble.showDot()); + } mFlyout.bringToFront(); }); mFlyout.removeCallbacks(mHideFlyout); @@ -2316,19 +2354,6 @@ public class BubbleStackView extends FrameLayout } } - private int getStatusBarHeight() { - if (getRootWindowInsets() != null) { - WindowInsets insets = getRootWindowInsets(); - return Math.max( - mStatusBarHeight, - insets.getDisplayCutout() != null - ? insets.getDisplayCutout().getSafeInsetTop() - : 0); - } - - return 0; - } - private void requestUpdate() { if (mViewUpdatedRequested || mIsExpansionAnimating) { return; @@ -2477,7 +2502,7 @@ public class BubbleStackView extends FrameLayout PhysicsAnimator.getInstance(mAnimatingOutSurfaceContainer).cancel(); mAnimatingOutSurfaceContainer.setScaleX(1f); mAnimatingOutSurfaceContainer.setScaleY(1f); - mAnimatingOutSurfaceContainer.setTranslationX(0); + mAnimatingOutSurfaceContainer.setTranslationX(mExpandedViewContainer.getPaddingLeft()); mAnimatingOutSurfaceContainer.setTranslationY(0); final int[] activityViewLocation = @@ -2535,9 +2560,22 @@ public class BubbleStackView extends FrameLayout Log.d(TAG, "updateExpandedView: mIsExpanded=" + mIsExpanded); } + // Need to update the padding around the view for any insets + Insets insets = mPositioner.getInsets(); + int leftPadding = insets.left + mExpandedViewPadding; + int rightPadding = insets.right + mExpandedViewPadding; + if (mPositioner.getOrientation() == Configuration.ORIENTATION_LANDSCAPE) { + if (!mStackAnimationController.isStackOnLeftSide()) { + rightPadding += mPointerHeight + mBubbleSize; + } else { + leftPadding += mPointerHeight + mBubbleSize; + } + } + mExpandedViewContainer.setPadding(leftPadding, 0, rightPadding, 0); mExpandedViewContainer.setVisibility(mIsExpanded ? VISIBLE : GONE); if (mExpandedBubble != null && mExpandedBubble.getExpandedView() != null) { mExpandedViewContainer.setTranslationY(getExpandedViewY()); + mExpandedViewContainer.setTranslationX(0f); mExpandedBubble.getExpandedView().updateView( mExpandedViewContainer.getLocationOnScreen()); } @@ -2580,12 +2618,27 @@ public class BubbleStackView extends FrameLayout if (index == -1) { return; } - float bubbleLeftFromScreenLeft = mExpandedAnimationController.getBubbleLeft(index); - float halfBubble = mBubbleSize / 2f; - float bubbleCenter = bubbleLeftFromScreenLeft + halfBubble; - // Padding might be adjusted for insets, so get it directly from the view - bubbleCenter -= mExpandedViewContainer.getPaddingLeft(); - mExpandedBubble.getExpandedView().setPointerPosition(bubbleCenter); + float bubblePosition = mExpandedAnimationController.getBubbleXOrYForOrientation(index); + if (mPositioner.getOrientation() == Configuration.ORIENTATION_LANDSCAPE) { + float x = mStackOnLeftOrWillBe + ? mPositioner.getAvailableRect().left + : mPositioner.getAvailableRect().right + - mExpandedViewContainer.getPaddingRight() + - mPointerHeight; + float bubbleCenter = bubblePosition - getExpandedViewY() + (mBubbleSize / 2f); + mExpandedBubble.getExpandedView().setPointerPosition( + x, + bubbleCenter, + true, + mStackOnLeftOrWillBe); + } else { + float bubbleCenter = bubblePosition + (mBubbleSize / 2f); + mExpandedBubble.getExpandedView().setPointerPosition( + bubbleCenter, + getExpandedViewY(), + false, + mStackOnLeftOrWillBe); + } } /** @@ -2614,7 +2667,7 @@ public class BubbleStackView extends FrameLayout * @return the normalized x-axis position of the bubble stack rounded to 4 decimal places. */ public float getNormalizedXPosition() { - return new BigDecimal(getStackPosition().x / mDisplaySize.x) + return new BigDecimal(getStackPosition().x / mPositioner.getAvailableRect().width()) .setScale(4, RoundingMode.CEILING.HALF_UP) .floatValue(); } @@ -2623,7 +2676,7 @@ public class BubbleStackView extends FrameLayout * @return the normalized y-axis position of the bubble stack rounded to 4 decimal places. */ public float getNormalizedYPosition() { - return new BigDecimal(getStackPosition().y / mDisplaySize.y) + return new BigDecimal(getStackPosition().y / mPositioner.getAvailableRect().height()) .setScale(4, RoundingMode.CEILING.HALF_UP) .floatValue(); } diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/Bubbles.java b/packages/SystemUI/src/com/android/systemui/bubbles/Bubbles.java index 39c750de28ac..4882abc51ed6 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/Bubbles.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/Bubbles.java @@ -23,6 +23,7 @@ import androidx.annotation.MainThread; import com.android.systemui.statusbar.ScrimView; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.phone.ScrimController; +import com.android.wm.shell.ShellTaskOrganizer; import java.util.List; @@ -129,5 +130,8 @@ public interface Bubbles { void setOverflowListener(BubbleData.Listener listener); /** The task listener for events in bubble tasks. **/ - MultiWindowTaskListener getTaskManager(); + ShellTaskOrganizer getTaskOrganizer(); + + /** Contains information to help position things on the screen. */ + BubblePositioner getPositioner(); } diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/MultiWindowTaskListener.java b/packages/SystemUI/src/com/android/systemui/bubbles/MultiWindowTaskListener.java deleted file mode 100644 index dff8becccb86..000000000000 --- a/packages/SystemUI/src/com/android/systemui/bubbles/MultiWindowTaskListener.java +++ /dev/null @@ -1,177 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.bubbles; - -import static com.android.wm.shell.ShellTaskOrganizer.TASK_LISTENER_TYPE_MULTI_WINDOW; - -import android.app.ActivityManager.RunningTaskInfo; -import android.app.ActivityTaskManager; -import android.os.Handler; -import android.os.RemoteException; -import android.util.ArrayMap; -import android.util.Log; -import android.view.SurfaceControl; -import android.window.TaskOrganizer; -import android.window.WindowContainerToken; - -import com.android.wm.shell.ShellTaskOrganizer; - -/** - * Manages tasks that are displayed in multi-window (e.g. bubbles). These are displayed in a - * {@link TaskView}. - * - * This class listens on {@link TaskOrganizer} callbacks for events. Once visible, these tasks will - * intercept back press events. - * - * @see android.app.WindowConfiguration#WINDOWING_MODE_MULTI_WINDOW - * @see TaskView - */ -// TODO: Place in com.android.wm.shell vs. com.android.wm.shell.bubbles on shell migration. -public class MultiWindowTaskListener implements ShellTaskOrganizer.TaskListener { - private static final String TAG = MultiWindowTaskListener.class.getSimpleName(); - - private static final boolean DEBUG = false; - - //TODO(b/170153209): Have shell listener allow per task registration and remove this. - public interface Listener { - void onTaskAppeared(RunningTaskInfo taskInfo, SurfaceControl leash); - void onTaskVanished(RunningTaskInfo taskInfo); - void onTaskInfoChanged(RunningTaskInfo taskInfo); - void onBackPressedOnTaskRoot(RunningTaskInfo taskInfo); - } - - private static class TaskData { - final RunningTaskInfo taskInfo; - final Listener listener; - - TaskData(RunningTaskInfo info, Listener l) { - taskInfo = info; - listener = l; - } - } - - private final Handler mHandler; - private final ShellTaskOrganizer mTaskOrganizer; - private final ArrayMap<WindowContainerToken, TaskData> mTasks = new ArrayMap<>(); - - private MultiWindowTaskListener.Listener mPendingListener; - - /** - * Create a listener for tasks in multi-window mode. - */ - public MultiWindowTaskListener(Handler handler, ShellTaskOrganizer organizer) { - mHandler = handler; - mTaskOrganizer = organizer; - mTaskOrganizer.addListener(this, TASK_LISTENER_TYPE_MULTI_WINDOW); - } - - /** - * @return the task organizer that is listened to. - */ - public TaskOrganizer getTaskOrganizer() { - return mTaskOrganizer; - } - - // TODO(b/129067201): track launches for bubbles - // Once we have key in ActivityOptions, match listeners via that key - public void setPendingListener(Listener listener) { - mPendingListener = listener; - } - - /** - * Removes a task listener previously registered when starting a new activity. - */ - public void removeListener(Listener listener) { - if (DEBUG) { - Log.d(TAG, "removeListener: listener=" + listener); - } - if (mPendingListener == listener) { - mPendingListener = null; - } - for (int i = 0; i < mTasks.size(); i++) { - if (mTasks.valueAt(i).listener == listener) { - mTasks.removeAt(i); - } - } - } - - @Override - public void onTaskAppeared(RunningTaskInfo taskInfo, SurfaceControl leash) { - if (DEBUG) { - Log.d(TAG, "onTaskAppeared: taskInfo=" + taskInfo - + " mPendingListener=" + mPendingListener); - } - if (mPendingListener == null) { - // If there is no pending listener, then we are either receiving this task as a part of - // registering the task org again (ie. after SysUI dies) or the previously started - // task is no longer needed (ie. bubble is closed soon after), for now, just finish the - // associated task - try { - ActivityTaskManager.getService().removeTask(taskInfo.taskId); - } catch (RemoteException e) { - Log.w(TAG, "Failed to remove taskId " + taskInfo.taskId); - } - return; - } - - mTaskOrganizer.setInterceptBackPressedOnTaskRoot(taskInfo.token, true); - - final TaskData data = new TaskData(taskInfo, mPendingListener); - mTasks.put(taskInfo.token, data); - mHandler.post(() -> data.listener.onTaskAppeared(taskInfo, leash)); - mPendingListener = null; - } - - @Override - public void onTaskVanished(RunningTaskInfo taskInfo) { - final TaskData data = mTasks.remove(taskInfo.token); - if (data == null) { - return; - } - - if (DEBUG) { - Log.d(TAG, "onTaskVanished: taskInfo=" + taskInfo + " listener=" + data.listener); - } - mHandler.post(() -> data.listener.onTaskVanished(taskInfo)); - } - - @Override - public void onTaskInfoChanged(RunningTaskInfo taskInfo) { - final TaskData data = mTasks.get(taskInfo.token); - if (data == null) { - return; - } - - if (DEBUG) { - Log.d(TAG, "onTaskInfoChanged: taskInfo=" + taskInfo + " listener=" + data.listener); - } - mHandler.post(() -> data.listener.onTaskInfoChanged(taskInfo)); - } - - @Override - public void onBackPressedOnTaskRoot(RunningTaskInfo taskInfo) { - final TaskData data = mTasks.get(taskInfo.token); - if (data == null) { - return; - } - - if (DEBUG) { - Log.d(TAG, "onTaskInfoChanged: taskInfo=" + taskInfo + " listener=" + data.listener); - } - mHandler.post(() -> data.listener.onBackPressedOnTaskRoot(taskInfo)); - } -} diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/StackEducationView.kt b/packages/SystemUI/src/com/android/systemui/bubbles/StackEducationView.kt index 9e7a2fb68d58..216df2e1f402 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/StackEducationView.kt +++ b/packages/SystemUI/src/com/android/systemui/bubbles/StackEducationView.kt @@ -128,7 +128,7 @@ class StackEducationView constructor(context: Context) : LinearLayout(context) { private fun setShouldShow(shouldShow: Boolean) { context.getSharedPreferences(context.packageName, Context.MODE_PRIVATE) - .edit().putBoolean(PREF_MANAGED_EDUCATION, !shouldShow).apply() + .edit().putBoolean(PREF_STACK_EDUCATION, !shouldShow).apply() } } diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/TaskView.java b/packages/SystemUI/src/com/android/systemui/bubbles/TaskView.java index 524fa42af7d5..85616d1513f5 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/TaskView.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/TaskView.java @@ -29,20 +29,27 @@ import android.content.Intent; import android.content.pm.LauncherApps; import android.content.pm.ShortcutInfo; import android.graphics.Rect; +import android.os.Binder; +import android.os.Handler; import android.view.SurfaceControl; import android.view.SurfaceHolder; import android.view.SurfaceView; import android.window.WindowContainerToken; import android.window.WindowContainerTransaction; +import com.android.wm.shell.ShellTaskOrganizer; + import dalvik.system.CloseGuard; +import java.io.PrintWriter; +import java.util.concurrent.Executor; + /** * View that can display a task. */ // TODO: Place in com.android.wm.shell vs. com.android.wm.shell.bubbles on shell migration. public class TaskView extends SurfaceView implements SurfaceHolder.Callback, - MultiWindowTaskListener.Listener { + ShellTaskOrganizer.TaskListener { public interface Listener { /** Called when the container is ready for launching activities. */ @@ -66,7 +73,7 @@ public class TaskView extends SurfaceView implements SurfaceHolder.Callback, private final CloseGuard mGuard = CloseGuard.get(); - private final MultiWindowTaskListener mMultiWindowTaskListener; + private final ShellTaskOrganizer mTaskOrganizer; private ActivityManager.RunningTaskInfo mTaskInfo; private WindowContainerToken mTaskToken; @@ -75,14 +82,16 @@ public class TaskView extends SurfaceView implements SurfaceHolder.Callback, private boolean mSurfaceCreated; private boolean mIsInitialized; private Listener mListener; + private final Executor mExecutor; private final Rect mTmpRect = new Rect(); private final Rect mTmpRootRect = new Rect(); - public TaskView(Context context, MultiWindowTaskListener taskListener) { + public TaskView(Context context, ShellTaskOrganizer organizer, Executor executor) { super(context, null, 0, 0, true /* disableBackgroundLayer */); - mMultiWindowTaskListener = taskListener; + mExecutor = executor; + mTaskOrganizer = organizer; setUseAlpha(); getHolder().addCallback(this); mGuard.open("release"); @@ -111,7 +120,6 @@ public class TaskView extends SurfaceView implements SurfaceHolder.Callback, */ public void startShortcutActivity(@NonNull ShortcutInfo shortcut, @NonNull ActivityOptions options, @Nullable Rect sourceBounds) { - mMultiWindowTaskListener.setPendingListener(this); prepareActivityOptions(options); LauncherApps service = mContext.getSystemService(LauncherApps.class); try { @@ -130,7 +138,6 @@ public class TaskView extends SurfaceView implements SurfaceHolder.Callback, */ public void startActivity(@NonNull PendingIntent pendingIntent, @Nullable Intent fillInIntent, @NonNull ActivityOptions options) { - mMultiWindowTaskListener.setPendingListener(this); prepareActivityOptions(options); try { pendingIntent.send(mContext, 0 /* code */, fillInIntent, @@ -142,6 +149,9 @@ public class TaskView extends SurfaceView implements SurfaceHolder.Callback, } private void prepareActivityOptions(ActivityOptions options) { + final Binder launchCookie = new Binder(); + mTaskOrganizer.setPendingLaunchCookieListener(launchCookie, this); + options.setLaunchCookie(launchCookie); options.setLaunchWindowingMode(WINDOWING_MODE_MULTI_WINDOW); options.setTaskAlwaysOnTop(true); } @@ -163,7 +173,7 @@ public class TaskView extends SurfaceView implements SurfaceHolder.Callback, WindowContainerTransaction wct = new WindowContainerTransaction(); wct.setBounds(mTaskToken, mTmpRect); // TODO(b/151449487): Enable synchronization - mMultiWindowTaskListener.getTaskOrganizer().applyTransaction(wct); + mTaskOrganizer.applyTransaction(wct); } /** @@ -187,7 +197,7 @@ public class TaskView extends SurfaceView implements SurfaceHolder.Callback, private void performRelease() { getHolder().removeCallback(this); - mMultiWindowTaskListener.removeListener(this); + mTaskOrganizer.removeListener(this); resetTaskInfo(); mGuard.close(); if (mListener != null && mIsInitialized) { @@ -205,7 +215,7 @@ public class TaskView extends SurfaceView implements SurfaceHolder.Callback, private void updateTaskVisibility() { WindowContainerTransaction wct = new WindowContainerTransaction(); wct.setHidden(mTaskToken, !mSurfaceCreated /* hidden */); - mMultiWindowTaskListener.getTaskOrganizer().applyTransaction(wct); + mTaskOrganizer.applyTransaction(wct); // TODO(b/151449487): Only call callback once we enable synchronization if (mListener != null) { mListener.onTaskVisibilityChanged(mTaskInfo.taskId, mSurfaceCreated); @@ -215,33 +225,37 @@ public class TaskView extends SurfaceView implements SurfaceHolder.Callback, @Override public void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) { - mTaskInfo = taskInfo; - mTaskToken = taskInfo.token; - mTaskLeash = leash; - - if (mSurfaceCreated) { - // Surface is ready, so just reparent the task to this surface control - mTransaction.reparent(mTaskLeash, getSurfaceControl()) - .show(mTaskLeash) - .apply(); - } else { - // The surface has already been destroyed before the task has appeared, so go ahead and - // hide the task entirely - updateTaskVisibility(); - } + mExecutor.execute(() -> { + mTaskInfo = taskInfo; + mTaskToken = taskInfo.token; + mTaskLeash = leash; + + if (mSurfaceCreated) { + // Surface is ready, so just reparent the task to this surface control + mTransaction.reparent(mTaskLeash, getSurfaceControl()) + .show(mTaskLeash) + .apply(); + } else { + // The surface has already been destroyed before the task has appeared, + // so go ahead and hide the task entirely + updateTaskVisibility(); + } - // TODO: Synchronize show with the resize - onLocationChanged(); - setResizeBackgroundColor(taskInfo.taskDescription.getBackgroundColor()); + // TODO: Synchronize show with the resize + onLocationChanged(); + setResizeBackgroundColor(taskInfo.taskDescription.getBackgroundColor()); - if (mListener != null) { - mListener.onTaskCreated(taskInfo.taskId, taskInfo.baseActivity); - } + if (mListener != null) { + mListener.onTaskCreated(taskInfo.taskId, taskInfo.baseActivity); + } + }); } @Override public void onTaskVanished(ActivityManager.RunningTaskInfo taskInfo) { - if (mTaskToken != null && mTaskToken.equals(taskInfo.token)) { + mExecutor.execute(() -> { + if (mTaskToken == null || !mTaskToken.equals(taskInfo.token)) return; + if (mListener != null) { mListener.onTaskRemovalStarted(taskInfo.taskId); } @@ -249,22 +263,37 @@ public class TaskView extends SurfaceView implements SurfaceHolder.Callback, // Unparent the task when this surface is destroyed mTransaction.reparent(mTaskLeash, null).apply(); resetTaskInfo(); - } + }); } @Override public void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) { - mTaskInfo.taskDescription = taskInfo.taskDescription; - setResizeBackgroundColor(taskInfo.taskDescription.getBackgroundColor()); + mExecutor.execute(() -> { + mTaskInfo.taskDescription = taskInfo.taskDescription; + setResizeBackgroundColor(taskInfo.taskDescription.getBackgroundColor()); + }); } @Override public void onBackPressedOnTaskRoot(ActivityManager.RunningTaskInfo taskInfo) { - if (mTaskToken != null && mTaskToken.equals(taskInfo.token)) { + mExecutor.execute(() -> { + if (mTaskToken == null || !mTaskToken.equals(taskInfo.token)) return; if (mListener != null) { mListener.onBackPressedOnTaskRoot(taskInfo.taskId); } - } + }); + } + + @Override + public void dump(@androidx.annotation.NonNull PrintWriter pw, String prefix) { + final String innerPrefix = prefix + " "; + final String childPrefix = innerPrefix + " "; + pw.println(prefix + this); + } + + @Override + public String toString() { + return "TaskView" + ":" + (mTaskInfo != null ? mTaskInfo.taskId : "null"); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java b/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java index fd73207a1e3e..5a70401abb4a 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java @@ -19,11 +19,9 @@ package com.android.systemui.bubbles.animation; import android.content.res.Configuration; import android.content.res.Resources; import android.graphics.Path; -import android.graphics.Point; import android.graphics.PointF; -import android.view.DisplayCutout; +import android.graphics.Rect; import android.view.View; -import android.view.WindowInsets; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -32,6 +30,7 @@ import androidx.dynamicanimation.animation.SpringForce; import com.android.systemui.Interpolators; import com.android.systemui.R; +import com.android.systemui.bubbles.BubblePositioner; import com.android.wm.shell.animation.PhysicsAnimator; import com.android.wm.shell.common.magnetictarget.MagnetizedObject; @@ -83,16 +82,8 @@ public class ExpandedAnimationController private float mBubblePaddingTop; /** Size of each bubble. */ private float mBubbleSizePx; - /** Space between bubbles in row above expanded view. */ - private float mSpaceBetweenBubbles; - /** Height of the status bar. */ - private float mStatusBarHeight; - /** Size of display. */ - private Point mDisplaySize; /** Max number of bubbles shown in row above expanded view. */ private int mBubblesMaxRendered; - /** What the current screen orientation is. */ - private int mScreenOrientation; private boolean mAnimatingExpand = false; @@ -104,7 +95,8 @@ public class ExpandedAnimationController private boolean mPreparingToCollapse = false; private boolean mAnimatingCollapse = false; - private @Nullable Runnable mAfterExpand; + @Nullable + private Runnable mAfterExpand; private Runnable mAfterCollapse; private PointF mCollapsePoint; @@ -138,9 +130,12 @@ public class ExpandedAnimationController */ private Runnable mOnBubbleAnimatedOutAction; - public ExpandedAnimationController(Point displaySize, int expandedViewPadding, - int orientation, Runnable onBubbleAnimatedOutAction) { - updateResources(orientation, displaySize); + private BubblePositioner mPositioner; + + public ExpandedAnimationController(BubblePositioner positioner, int expandedViewPadding, + Runnable onBubbleAnimatedOutAction) { + mPositioner = positioner; + updateResources(); mExpandedViewPadding = expandedViewPadding; mOnBubbleAnimatedOutAction = onBubbleAnimatedOutAction; } @@ -152,7 +147,8 @@ public class ExpandedAnimationController private boolean mBubbleDraggedOutEnough = false; /** End action to run when the lead bubble's expansion animation completes. */ - @Nullable private Runnable mLeadBubbleEndAction; + @Nullable + private Runnable mLeadBubbleEndAction; /** * Animates expanding the bubbles into a row along the top of the screen, optionally running an @@ -200,28 +196,17 @@ public class ExpandedAnimationController /** * Update effective screen width based on current orientation. - * @param orientation Landscape or portrait. - * @param displaySize Updated display size. */ - public void updateResources(int orientation, Point displaySize) { - mScreenOrientation = orientation; - mDisplaySize = displaySize; + public void updateResources() { if (mLayout == null) { return; } Resources res = mLayout.getContext().getResources(); mBubblePaddingTop = res.getDimensionPixelSize(R.dimen.bubble_padding_top); - mStatusBarHeight = res.getDimensionPixelSize( - com.android.internal.R.dimen.status_bar_height); mStackOffsetPx = res.getDimensionPixelSize(R.dimen.bubble_stack_offset); mBubblePaddingTop = res.getDimensionPixelSize(R.dimen.bubble_padding_top); mBubbleSizePx = res.getDimensionPixelSize(R.dimen.individual_bubble_size); mBubblesMaxRendered = res.getInteger(R.integer.bubbles_max_rendered); - - // Includes overflow button. - float totalGapWidth = getWidthForDisplayingBubbles() - (mExpandedViewPadding * 2) - - (mBubblesMaxRendered + 1) * mBubbleSizePx; - mSpaceBetweenBubbles = totalGapWidth / mBubblesMaxRendered; } /** @@ -270,20 +255,27 @@ public class ExpandedAnimationController // If we're expanding, first draw a line from the bubble's current position to the // top of the screen. path.lineTo(bubble.getTranslationX(), expandedY); - // Then, draw a line across the screen to the bubble's resting position. - path.lineTo(getBubbleLeft(index), expandedY); + if (mPositioner.getOrientation() == Configuration.ORIENTATION_LANDSCAPE) { + Rect availableRect = mPositioner.getAvailableRect(); + boolean onLeft = mCollapsePoint != null + && mCollapsePoint.x < (availableRect.width() / 2f); + float translationX = onLeft + ? availableRect.left + mExpandedViewPadding + : availableRect.right - mBubbleSizePx - mExpandedViewPadding; + path.lineTo(translationX, getBubbleXOrYForOrientation(index)); + } else { + path.lineTo(getBubbleXOrYForOrientation(index), expandedY); + } } else { - final float sideMultiplier = - mLayout.isFirstChildXLeftOfCenter(mCollapsePoint.x) ? -1 : 1; - final float stackedX = mCollapsePoint.x + (sideMultiplier * index * mStackOffsetPx); + final float stackedX = mCollapsePoint.x; // If we're collapsing, draw a line from the bubble's current position to the side // of the screen where the bubble will be stacked. path.lineTo(stackedX, expandedY); // Then, draw a line down to the stack position. - path.lineTo(stackedX, mCollapsePoint.y); + path.lineTo(stackedX, mCollapsePoint.y + index * mStackOffsetPx); } // The lead bubble should be the bubble with the longest distance to travel when we're @@ -413,7 +405,8 @@ public class ExpandedAnimationController updateBubblePositions(); } - @Nullable public View getDraggedOutBubble() { + @Nullable + public View getDraggedOutBubble() { return mMagnetizedBubbleDraggingOut == null ? null : mMagnetizedBubbleDraggingOut.getUnderlyingObject(); @@ -432,7 +425,7 @@ public class ExpandedAnimationController final int index = mLayout.indexOfChild(bubbleView); animationForChildAtIndex(index) - .position(getBubbleLeft(index), getExpandedY()) + .position(getBubbleXOrYForOrientation(index), getExpandedY()) .withPositionStartVelocities(velX, velY) .start(() -> bubbleView.setTranslationZ(0f) /* after */); @@ -459,15 +452,7 @@ public class ExpandedAnimationController /** The Y value of the row of expanded bubbles. */ public float getExpandedY() { - if (mLayout == null || mLayout.getRootWindowInsets() == null) { - return 0; - } - final WindowInsets insets = mLayout.getRootWindowInsets(); - return mBubblePaddingTop + Math.max( - mStatusBarHeight, - insets.getDisplayCutout() != null - ? insets.getDisplayCutout().getSafeInsetTop() - : 0); + return mPositioner.getAvailableRect().top + mBubblePaddingTop; } /** Description of current animation controller state. */ @@ -481,7 +466,7 @@ public class ExpandedAnimationController @Override void onActiveControllerForLayout(PhysicsAnimationLayout layout) { - updateResources(mScreenOrientation, mDisplaySize); + updateResources(); // Ensure that all child views are at 1x scale, and visible, in case they were animating // in. @@ -526,7 +511,7 @@ public class ExpandedAnimationController } else if (mAnimatingCollapse) { startOrUpdatePathAnimation(false /* expanding */); } else { - child.setTranslationX(getBubbleLeft(index)); + child.setTranslationX(getBubbleXOrYForOrientation(index)); // If we're preparing to collapse, don't start animations since the collapse animation // will take over and animate the new bubble into the correct (stacked) position. @@ -595,76 +580,56 @@ public class ExpandedAnimationController return; } - animationForChild(bubble) - .translationX(getBubbleLeft(i)) - .start(); - } - } - - /** - * @param index Bubble index in row. - * @return Bubble left x from left edge of screen. - */ - public float getBubbleLeft(int index) { - final float bubbleFromRowLeft = index * (mBubbleSizePx + mSpaceBetweenBubbles); - return getRowLeft() + bubbleFromRowLeft; - } - - /** - * When expanded, the bubbles are centered in the screen. In portrait, all available space is - * used. In landscape we have too much space so the value is restricted. This method accounts - * for window decorations (nav bar, cutouts). - * - * @return the desired width to display the expanded bubbles in. - */ - public float getWidthForDisplayingBubbles() { - final float availableWidth = getAvailableScreenWidth(true /* includeStableInsets */); - if (mScreenOrientation == Configuration.ORIENTATION_LANDSCAPE) { - // display size y in landscape will be the smaller dimension of the screen - return Math.max(mDisplaySize.y, availableWidth * CENTER_BUBBLES_LANDSCAPE_PERCENT); - } else { - return availableWidth; + if (mPositioner.getOrientation() == Configuration.ORIENTATION_LANDSCAPE) { + Rect availableRect = mPositioner.getAvailableRect(); + boolean onLeft = mCollapsePoint != null + && mCollapsePoint.x < (availableRect.width() / 2f); + animationForChild(bubble) + .translationX(onLeft + ? availableRect.left + mExpandedViewPadding + : availableRect.right - mBubbleSizePx - mExpandedViewPadding) + .translationY(getBubbleXOrYForOrientation(i)) + .start(); + } else { + animationForChild(bubble) + .translationX(getBubbleXOrYForOrientation(i)) + .translationY(getExpandedY()) + .start(); + } } } /** - * Determines the available screen width without the cutout. + * When bubbles are expanded in portrait, they display at the top of the screen in a horizontal + * row. When in landscape, they show at the left or right side in a vertical row. This method + * accounts for screen orientation and will return an x or y value for the position of the + * bubble in the row. * - * @param subtractStableInsets Whether or not stable insets should also be removed from the - * returned width. - * @return the total screen width available accounting for cutouts and insets, - * iff {@param includeStableInsets} is true. + * @param index Bubble index in row. + * @return the y position of the bubble if {@link Configuration#ORIENTATION_LANDSCAPE} and the + * x position if {@link Configuration#ORIENTATION_PORTRAIT}. */ - private float getAvailableScreenWidth(boolean subtractStableInsets) { - float availableSize = mDisplaySize.x; - WindowInsets insets = mLayout != null ? mLayout.getRootWindowInsets() : null; - if (insets != null) { - int cutoutLeft = 0; - int cutoutRight = 0; - DisplayCutout cutout = insets.getDisplayCutout(); - if (cutout != null) { - cutoutLeft = cutout.getSafeInsetLeft(); - cutoutRight = cutout.getSafeInsetRight(); - } - final int stableLeft = subtractStableInsets ? insets.getStableInsetLeft() : 0; - final int stableRight = subtractStableInsets ? insets.getStableInsetRight() : 0; - availableSize -= Math.max(stableLeft, cutoutLeft); - availableSize -= Math.max(stableRight, cutoutRight); - } - return availableSize; - } - - private float getRowLeft() { + public float getBubbleXOrYForOrientation(int index) { if (mLayout == null) { return 0; } - float rowWidth = (mLayout.getChildCount() * mBubbleSizePx) - + ((mLayout.getChildCount() - 1) * mSpaceBetweenBubbles); - - // This display size we're using includes the size of the insets, we want the true - // center of the display minus the notch here, which means we should include the - // stable insets (e.g. status bar, nav bar) in this calculation. - final float trueCenter = getAvailableScreenWidth(false /* subtractStableInsets */) / 2f; - return trueCenter - (rowWidth / 2f); + Rect availableRect = mPositioner.getAvailableRect(); + final boolean isLandscape = + mPositioner.getOrientation() == Configuration.ORIENTATION_LANDSCAPE; + final float availableSpace = isLandscape + ? availableRect.height() + : availableRect.width(); + final float spaceForMaxBubbles = (mExpandedViewPadding * 2) + + (mBubblesMaxRendered + 1) * mBubbleSizePx; + final float spaceBetweenBubbles = + (availableSpace - spaceForMaxBubbles) / mBubblesMaxRendered; + final float expandedStackSize = (mLayout.getChildCount() * mBubbleSizePx) + + ((mLayout.getChildCount() - 1) * spaceBetweenBubbles); + final float centerPosition = isLandscape + ? availableRect.centerY() + : availableRect.centerX(); + final float rowStart = centerPosition - (expandedStackSize / 2f); + final float positionInBar = index * (mBubbleSizePx + spaceBetweenBubbles); + return rowStart + positionInBar; } } diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java b/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java index 4c902b99d4ea..31e1ca839e5d 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java @@ -24,7 +24,6 @@ import android.graphics.RectF; import android.provider.Settings; import android.util.Log; import android.view.View; -import android.view.WindowInsets; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -35,6 +34,7 @@ import androidx.dynamicanimation.animation.SpringAnimation; import androidx.dynamicanimation.animation.SpringForce; import com.android.systemui.R; +import com.android.systemui.bubbles.BubblePositioner; import com.android.systemui.bubbles.BubbleStackView; import com.android.wm.shell.animation.PhysicsAnimator; import com.android.wm.shell.common.FloatingContentCoordinator; @@ -59,12 +59,6 @@ public class StackAnimationController extends private static final String TAG = "Bubbs.StackCtrl"; - /** Scale factor to use initially for new bubbles being animated in. */ - private static final float ANIMATE_IN_STARTING_SCALE = 1.15f; - - /** Translation factor (multiplied by stack offset) to use for bubbles being animated in/out. */ - private static final int ANIMATE_TRANSLATION_FACTOR = 4; - /** Values to use for animating bubbles in. */ private static final float ANIMATE_IN_STIFFNESS = 1000f; private static final int ANIMATE_IN_START_DELAY = 25; @@ -198,10 +192,8 @@ public class StackAnimationController extends private int mBubblePaddingTop; /** How far offscreen the stack rests. */ private int mBubbleOffscreen; - /** How far down the screen the stack starts, when there is no pre-existing location. */ - private int mStackStartingVerticalOffset; - /** Height of the status bar. */ - private float mStatusBarHeight; + /** Contains display size, orientation, and inset information. */ + private BubblePositioner mPositioner; /** FloatingContentCoordinator instance for resolving floating content conflicts. */ private FloatingContentCoordinator mFloatingContentCoordinator; @@ -266,10 +258,12 @@ public class StackAnimationController extends public StackAnimationController( FloatingContentCoordinator floatingContentCoordinator, IntSupplier bubbleCountSupplier, - Runnable onBubbleAnimatedOutAction) { + Runnable onBubbleAnimatedOutAction, + BubblePositioner positioner) { mFloatingContentCoordinator = floatingContentCoordinator; mBubbleCountSupplier = bubbleCountSupplier; mOnBubbleAnimatedOutAction = onBubbleAnimatedOutAction; + mPositioner = positioner; } /** @@ -583,45 +577,12 @@ public class StackAnimationController extends * be animated or dragged beyond them. */ public RectF getAllowableStackPositionRegion() { - final WindowInsets insets = mLayout.getRootWindowInsets(); - final RectF allowableRegion = new RectF(); - if (insets != null) { - allowableRegion.left = - -mBubbleOffscreen - + Math.max( - insets.getSystemWindowInsetLeft(), - insets.getDisplayCutout() != null - ? insets.getDisplayCutout().getSafeInsetLeft() - : 0); - allowableRegion.right = - mLayout.getWidth() - - mBubbleSize - + mBubbleOffscreen - - Math.max( - insets.getSystemWindowInsetRight(), - insets.getDisplayCutout() != null - ? insets.getDisplayCutout().getSafeInsetRight() - : 0); - - allowableRegion.top = - mBubblePaddingTop - + Math.max( - mStatusBarHeight, - insets.getDisplayCutout() != null - ? insets.getDisplayCutout().getSafeInsetTop() - : 0); - allowableRegion.bottom = - mLayout.getHeight() - - mBubbleSize - - mBubblePaddingTop - - (mImeHeight != UNSET ? mImeHeight + mBubblePaddingTop : 0f) - - Math.max( - insets.getStableInsetBottom(), - insets.getDisplayCutout() != null - ? insets.getDisplayCutout().getSafeInsetBottom() - : 0); - } - + final RectF allowableRegion = new RectF(mPositioner.getAvailableRect()); + allowableRegion.left -= mBubbleOffscreen; + allowableRegion.top += mBubblePaddingTop; + allowableRegion.right += mBubbleOffscreen - mBubbleSize; + allowableRegion.bottom -= mBubblePaddingTop + mBubbleSize + + (mImeHeight != UNSET ? mImeHeight + mBubblePaddingTop : 0f); return allowableRegion; } @@ -744,15 +705,13 @@ public class StackAnimationController extends @Override float getOffsetForChainedPropertyAnimation(DynamicAnimation.ViewProperty property) { - if (property.equals(DynamicAnimation.TRANSLATION_X)) { + if (property.equals(DynamicAnimation.TRANSLATION_Y)) { // If we're in the dismiss target, have the bubbles pile on top of each other with no // offset. if (isStackStuckToTarget()) { return 0f; } else { - // Offset to the left if we're on the left, or the right otherwise. - return mLayout.isFirstChildXLeftOfCenter(mStackPosition.x) - ? -mStackOffset : mStackOffset; + return mStackOffset; } } else { return 0f; @@ -826,22 +785,15 @@ public class StackAnimationController extends mBubbleBitmapSize = res.getDimensionPixelSize(R.dimen.bubble_bitmap_size); mBubblePaddingTop = res.getDimensionPixelSize(R.dimen.bubble_padding_top); mBubbleOffscreen = res.getDimensionPixelSize(R.dimen.bubble_stack_offscreen); - mStackStartingVerticalOffset = - res.getDimensionPixelSize(R.dimen.bubble_stack_starting_offset_y); - mStatusBarHeight = - res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height); } /** - * Update effective screen width based on current orientation. - * @param orientation Landscape or portrait. + * Update resources. */ - public void updateResources(int orientation) { + public void updateResources() { if (mLayout != null) { Resources res = mLayout.getContext().getResources(); mBubblePaddingTop = res.getDimensionPixelSize(R.dimen.bubble_padding_top); - mStatusBarHeight = res.getDimensionPixelSize( - com.android.internal.R.dimen.status_bar_height); } } @@ -978,19 +930,19 @@ public class StackAnimationController extends return; } - final float xOffset = - getOffsetForChainedPropertyAnimation(DynamicAnimation.TRANSLATION_X); + final float yOffset = + getOffsetForChainedPropertyAnimation(DynamicAnimation.TRANSLATION_Y); // Position the new bubble in the correct position, scaled down completely. - child.setTranslationX(mStackPosition.x + xOffset * index); - child.setTranslationY(mStackPosition.y); + child.setTranslationX(mStackPosition.x); + child.setTranslationY(mStackPosition.y + yOffset * index); child.setScaleX(0f); child.setScaleY(0f); // Push the subsequent views out of the way, if there are subsequent views. if (index + 1 < mLayout.getChildCount()) { animationForChildAtIndex(index + 1) - .translationX(mStackPosition.x + xOffset * (index + 1)) + .translationY(mStackPosition.y + yOffset * (index + 1)) .withStiffness(SpringForce.STIFFNESS_LOW) .start(); } diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java index cb90b6114396..3aa462657637 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java @@ -38,7 +38,6 @@ import android.view.accessibility.AccessibilityManager; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.UiEventLogger; -import com.android.internal.logging.UiEventLoggerImpl; import com.android.internal.util.NotificationMessagingUtil; import com.android.internal.widget.LockPatternUtils; import com.android.keyguard.ViewMediatorCallback; @@ -340,13 +339,6 @@ public class DependencyProvider { return Choreographer.getInstance(); } - /** Provides an instance of {@link com.android.internal.logging.UiEventLogger} */ - @Provides - @SysUISingleton - static UiEventLogger provideUiEventLogger() { - return new UiEventLoggerImpl(); - } - /** */ @Provides @SysUISingleton diff --git a/packages/SystemUI/src/com/android/systemui/dagger/GlobalModule.java b/packages/SystemUI/src/com/android/systemui/dagger/GlobalModule.java index c5dc8cccfdf4..554d9cbb454f 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/GlobalModule.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/GlobalModule.java @@ -16,9 +16,20 @@ package com.android.systemui.dagger; +import android.content.Context; +import android.util.DisplayMetrics; + +import com.android.internal.logging.UiEventLogger; +import com.android.internal.logging.UiEventLoggerImpl; import com.android.systemui.util.concurrency.GlobalConcurrencyModule; +import com.android.wm.shell.WindowManagerShellWrapper; +import com.android.wm.shell.animation.FlingAnimationUtils; +import com.android.wm.shell.common.FloatingContentCoordinator; + +import javax.inject.Singleton; import dagger.Module; +import dagger.Provides; /** * Supplies globally scoped instances that should be available in all versions of SystemUI @@ -39,4 +50,38 @@ import dagger.Module; FrameworkServicesModule.class, GlobalConcurrencyModule.class}) public class GlobalModule { + + // TODO(b/161980186): Currently only used by Bubbles, can move back to WMShellBaseModule once + // Bubbles has migrated over + @Singleton + @Provides + static FloatingContentCoordinator provideFloatingContentCoordinator() { + return new FloatingContentCoordinator(); + } + + // TODO(b/161980186): Currently only used by Bubbles, can move back to WMShellBaseModule once + // Bubbles has migrated over + @Singleton + @Provides + static WindowManagerShellWrapper provideWindowManagerShellWrapper() { + return new WindowManagerShellWrapper(); + } + + // TODO(b/162923491): This should not be a singleton at all, the display metrics can change and + // callers should be creating a new builder on demand + @Singleton + @Provides + static FlingAnimationUtils.Builder provideFlingAnimationUtilsBuilder( + Context context) { + DisplayMetrics displayMetrics = new DisplayMetrics(); + context.getDisplay().getMetrics(displayMetrics); + return new FlingAnimationUtils.Builder(displayMetrics); + } + + /** Provides an instance of {@link com.android.internal.logging.UiEventLogger} */ + @Provides + @Singleton + static UiEventLogger provideUiEventLogger() { + return new UiEventLoggerImpl(); + } } diff --git a/packages/SystemUI/src/com/android/systemui/dagger/GlobalRootComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/GlobalRootComponent.java index 00fdf55b28e0..d648c949ffc5 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/GlobalRootComponent.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/GlobalRootComponent.java @@ -52,7 +52,7 @@ public interface GlobalRootComponent { WMComponent.Builder getWMComponentBuilder(); /** - * Builder for a SysuiComponent. + * Builder for a SysUIComponent. */ SysUIComponent.Builder getSysUIComponent(); diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java index 4bea0674e8bf..b098579e74c3 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java @@ -22,8 +22,15 @@ import com.android.systemui.InitController; import com.android.systemui.SystemUIAppComponentFactory; import com.android.systemui.dump.DumpManager; import com.android.systemui.keyguard.KeyguardSliceProvider; +import com.android.systemui.shared.system.InputConsumerController; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.util.InjectionInflationController; +import com.android.wm.shell.ShellTaskOrganizer; +import com.android.wm.shell.onehanded.OneHanded; +import com.android.wm.shell.pip.Pip; +import com.android.wm.shell.splitscreen.SplitScreen; + +import java.util.Optional; import dagger.BindsInstance; import dagger.Subcomponent; @@ -43,15 +50,35 @@ public interface SysUIComponent { /** * Builder for a SysUIComponent. */ + @SysUISingleton @Subcomponent.Builder interface Builder { @BindsInstance - Builder setStubAPIClass(WMComponent.StubAPIClass stubAPIClass); + Builder setPip(Optional<Pip> p); + + @BindsInstance + Builder setSplitScreen(Optional<SplitScreen> s); + + @BindsInstance + Builder setOneHanded(Optional<OneHanded> o); + + @BindsInstance + Builder setInputConsumerController(InputConsumerController i); + + @BindsInstance + Builder setShellTaskOrganizer(ShellTaskOrganizer s); SysUIComponent build(); } /** + * Initializes all the SysUI components. + */ + default void init() { + // Do nothing + } + + /** * Provides a BootCompleteCache. */ @SysUISingleton diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java index 2c0b04fed810..7ca8e63bfae1 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java @@ -33,6 +33,7 @@ import com.android.systemui.demomode.DemoModeController; import com.android.systemui.dock.DockManager; import com.android.systemui.dock.DockManagerImpl; import com.android.systemui.doze.DozeHost; +import com.android.systemui.media.dagger.MediaModule; import com.android.systemui.plugins.qs.QSFactory; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.power.EnhancedEstimates; @@ -61,7 +62,6 @@ import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.DeviceProvisionedController; import com.android.systemui.statusbar.policy.DeviceProvisionedControllerImpl; import com.android.systemui.statusbar.policy.HeadsUpManager; -import com.android.systemui.wmshell.WMShellModule; import javax.inject.Named; @@ -74,9 +74,9 @@ import dagger.Provides; * overridden by the System UI implementation. */ @Module(includes = { - QSModule.class, - WMShellModule.class - }) + MediaModule.class, + QSModule.class +}) public abstract class SystemUIDefaultModule { @SysUISingleton diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java index 63d9a831b33f..a982ec5c0194 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java @@ -21,6 +21,7 @@ import com.android.systemui.BootCompleteCache; import com.android.systemui.BootCompleteCacheImpl; import com.android.systemui.appops.dagger.AppOpsModule; import com.android.systemui.assist.AssistModule; +import com.android.systemui.bubbles.Bubbles; import com.android.systemui.controls.dagger.ControlsModule; import com.android.systemui.demomode.dagger.DemoModeModule; import com.android.systemui.doze.dagger.DozeComponent; @@ -125,6 +126,9 @@ public abstract class SystemUIModule { @BindsOptionalOf abstract StatusBar optionalStatusBar(); + @BindsOptionalOf + abstract Bubbles optionalBubbles(); + @SysUISingleton @Binds abstract SystemClock bindSystemClock(SystemClockImpl systemClock); diff --git a/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java index ad90eff3c969..e3bd1b2b7db2 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java @@ -16,7 +16,15 @@ package com.android.systemui.dagger; -import javax.inject.Inject; +import com.android.systemui.shared.system.InputConsumerController; +import com.android.systemui.wmshell.WMShellModule; +import com.android.wm.shell.ShellTaskOrganizer; +import com.android.wm.shell.common.DisplayImeController; +import com.android.wm.shell.onehanded.OneHanded; +import com.android.wm.shell.pip.Pip; +import com.android.wm.shell.splitscreen.SplitScreen; + +import java.util.Optional; import dagger.Subcomponent; @@ -24,7 +32,7 @@ import dagger.Subcomponent; * Dagger Subcomponent for WindowManager. */ @WMSingleton -@Subcomponent(modules = {}) +@Subcomponent(modules = {WMShellModule.class}) public interface WMComponent { /** @@ -35,18 +43,36 @@ public interface WMComponent { WMComponent build(); } - /** - * Example class used for passing an API to SysUI from WMShell. - * - * TODO: Remove this once real WM classes are ready to go. - **/ - @WMSingleton - class StubAPIClass { - @Inject - StubAPIClass() {} + * Initializes all the WMShell components before starting any of the SystemUI components. + */ + default void init() { + // This is to prevent circular init problem by separating registration step out of its + // constructor. And make sure the initialization of DisplayImeController won't depend on + // specific feature anymore. + getDisplayImeController().startMonitorDisplays(); + getShellTaskOrganizer().registerOrganizer(); } - /** Create a StubAPIClass. */ - StubAPIClass createStubAPIClass(); + // Required components to be initialized at start up + @WMSingleton + ShellTaskOrganizer getShellTaskOrganizer(); + + @WMSingleton + DisplayImeController getDisplayImeController(); + + @WMSingleton + InputConsumerController getInputConsumerController(); + + // TODO(b/162923491): We currently pass the instances through to SysUI, but that may change + // depending on the threading mechanism we go with + + @WMSingleton + Optional<OneHanded> getOneHanded(); + + @WMSingleton + Optional<Pip> getPip(); + + @WMSingleton + Optional<SplitScreen> getSplitScreen(); } diff --git a/packages/SystemUI/src/com/android/keyguard/dagger/RootView.java b/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/RootView.java index 5ebff097604b..e6c46c07fff8 100644 --- a/packages/SystemUI/src/com/android/keyguard/dagger/RootView.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/RootView.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 The Android Open Source Project + * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.keyguard.dagger; +package com.android.systemui.dagger.qualifiers; import static java.lang.annotation.RetentionPolicy.RUNTIME; diff --git a/packages/SystemUI/src/com/android/systemui/doze/dagger/DozeScope.java b/packages/SystemUI/src/com/android/systemui/doze/dagger/DozeScope.java index 7a8b8166a969..435859afc03f 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/dagger/DozeScope.java +++ b/packages/SystemUI/src/com/android/systemui/doze/dagger/DozeScope.java @@ -24,7 +24,7 @@ import java.lang.annotation.Retention; import javax.inject.Scope; /** - * Scope annotation for singleton items within the StatusBarComponent. + * Scope annotation for singleton items within the DozeComponent. */ @Documented @Retention(RUNTIME) diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java index b55b29a80410..a330be6449e2 100644 --- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java +++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java @@ -140,8 +140,14 @@ public class GlobalActionsImpl implements GlobalActions, CommandQueue.Callbacks d.setContentView(R.layout.shutdown_dialog); d.setCancelable(false); - int color = Utils.getColorAttrDefaultColor(mContext, - com.android.systemui.R.attr.wallpaperTextColor); + int color; + if (mBlurUtils.supportsBlursOnWindows()) { + color = Utils.getColorAttrDefaultColor(mContext, + com.android.systemui.R.attr.wallpaperTextColor); + } else { + color = mContext.getResources().getColor( + com.android.systemui.R.color.global_actions_shutdown_ui_text); + } ProgressBar bar = d.findViewById(R.id.progress); bar.getIndeterminateDrawable().setTint(color); diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java index 5f726cd1e1f9..37bcb163d6f3 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java @@ -316,7 +316,8 @@ public class KeyguardSliceProvider extends SliceProvider implements } mDatePattern = getContext().getString(R.string.system_ui_aod_date_pattern); mPendingIntent = PendingIntent.getActivity(getContext(), 0, - new Intent(getContext(), KeyguardSliceProvider.class), 0); + new Intent(getContext(), KeyguardSliceProvider.class), + PendingIntent.FLAG_IMMUTABLE); try { //TODO(b/168778439): Remove this whole try catch. This is for debugging in dogfood. mMediaManager.addCallback(this); diff --git a/packages/SystemUI/src/com/android/systemui/media/KeyguardMediaController.kt b/packages/SystemUI/src/com/android/systemui/media/KeyguardMediaController.kt index 094ece27fc8e..6fb86504ec32 100644 --- a/packages/SystemUI/src/com/android/systemui/media/KeyguardMediaController.kt +++ b/packages/SystemUI/src/com/android/systemui/media/KeyguardMediaController.kt @@ -18,6 +18,7 @@ package com.android.systemui.media import android.view.View import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.media.dagger.MediaModule.KEYGUARD import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.statusbar.NotificationLockscreenUserManager import com.android.systemui.statusbar.StatusBarState @@ -25,6 +26,7 @@ import com.android.systemui.statusbar.SysuiStatusBarStateController import com.android.systemui.statusbar.notification.stack.MediaHeaderView import com.android.systemui.statusbar.phone.KeyguardBypassController import javax.inject.Inject +import javax.inject.Named /** * A class that controls the media notifications on the lock screen, handles its visibility and @@ -32,7 +34,7 @@ import javax.inject.Inject */ @SysUISingleton class KeyguardMediaController @Inject constructor( - private val mediaHost: MediaHost, + @param:Named(KEYGUARD) private val mediaHost: MediaHost, private val bypassController: KeyguardBypassController, private val statusBarStateController: SysuiStatusBarStateController, private val notifLockscreenUserManager: NotificationLockscreenUserManager diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselScrollHandler.kt b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselScrollHandler.kt index d80aafb714d3..cb14f31abd16 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselScrollHandler.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselScrollHandler.kt @@ -282,10 +282,12 @@ class MediaCarouselScrollHandler( scrollXAmount = -1 * relativePos } if (scrollXAmount != 0) { + val dx = if (isRtl) -scrollXAmount else scrollXAmount + val newScrollX = scrollView.relativeScrollX + dx // Delay the scrolling since scrollView calls springback which cancels // the animation again.. mainExecutor.execute { - scrollView.smoothScrollBy(if (isRtl) -scrollXAmount else scrollXAmount, 0) + scrollView.smoothScrollTo(newScrollX, scrollView.scrollY) } } val currentTranslation = scrollView.getContentTranslation() @@ -553,4 +555,4 @@ class MediaCarouselScrollHandler( } } } -}
\ No newline at end of file +} diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java index 5b096ea363b6..c18a6a45e286 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java +++ b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java @@ -354,7 +354,15 @@ public class MediaControlPanel { final MediaController controller = getController(); mBackgroundExecutor.execute(() -> mSeekBarViewModel.updateController(controller)); + // Guts label + boolean isDismissible = data.isClearable(); + mViewHolder.getSettingsText().setText(isDismissible + ? R.string.controls_media_close_session + : R.string.controls_media_active_session); + // Dismiss + mViewHolder.getDismissLabel().setAlpha(isDismissible ? 1 : DISABLED_ALPHA); + mViewHolder.getDismiss().setEnabled(isDismissible); mViewHolder.getDismiss().setOnClickListener(v -> { if (mKey != null) { closeGuts(); diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaHost.kt b/packages/SystemUI/src/com/android/systemui/media/MediaHost.kt index ce184aa23a57..857c50fc8d32 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaHost.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaHost.kt @@ -11,7 +11,7 @@ import com.android.systemui.util.animation.UniqueObjectHostView import java.util.Objects import javax.inject.Inject -class MediaHost @Inject constructor( +class MediaHost constructor( private val state: MediaHostStateHolder, private val mediaHierarchyManager: MediaHierarchyManager, private val mediaDataManager: MediaDataManager, diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaTimeoutListener.kt b/packages/SystemUI/src/com/android/systemui/media/MediaTimeoutListener.kt index 51dbfa733541..ce72991d01c0 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaTimeoutListener.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaTimeoutListener.kt @@ -46,7 +46,7 @@ class MediaTimeoutListener @Inject constructor( /** * Callback representing that a media object is now expired: * @param token Media session unique identifier - * @param pauseTimeuot True when expired for {@code PAUSED_MEDIA_TIMEOUT} + * @param pauseTimeout True when expired for {@code PAUSED_MEDIA_TIMEOUT} */ lateinit var timeoutCallback: (String, Boolean) -> Unit @@ -57,11 +57,10 @@ class MediaTimeoutListener @Inject constructor( // Having an old key means that we're migrating from/to resumption. We should update // the old listener to make sure that events will be dispatched to the new location. val migrating = oldKey != null && key != oldKey - var wasPlaying = false if (migrating) { val reusedListener = mediaListeners.remove(oldKey) if (reusedListener != null) { - wasPlaying = reusedListener.playing ?: false + val wasPlaying = reusedListener.playing ?: false if (DEBUG) Log.d(TAG, "migrating key $oldKey to $key, for resumption") reusedListener.mediaData = data reusedListener.key = key @@ -159,9 +158,8 @@ class MediaTimeoutListener @Inject constructor( Log.v(TAG, "Execute timeout for $key") } timedOut = true - if (dispatchEvents) { - timeoutCallback(key, timedOut) - } + // this event is async, so it's safe even when `dispatchEvents` is false + timeoutCallback(key, timedOut) }, PAUSED_MEDIA_TIMEOUT) } else { expireMediaTimeout(key, "playback started - $state, $key") diff --git a/packages/SystemUI/src/com/android/systemui/media/PlayerViewHolder.kt b/packages/SystemUI/src/com/android/systemui/media/PlayerViewHolder.kt index 666a6038a8b6..16327bd9064a 100644 --- a/packages/SystemUI/src/com/android/systemui/media/PlayerViewHolder.kt +++ b/packages/SystemUI/src/com/android/systemui/media/PlayerViewHolder.kt @@ -60,8 +60,10 @@ class PlayerViewHolder private constructor(itemView: View) { val action4 = itemView.requireViewById<ImageButton>(R.id.action4) // Settings screen + val settingsText = itemView.requireViewById<TextView>(R.id.remove_text) val cancel = itemView.requireViewById<View>(R.id.cancel) - val dismiss = itemView.requireViewById<View>(R.id.dismiss) + val dismiss = itemView.requireViewById<ViewGroup>(R.id.dismiss) + val dismissLabel = dismiss.getChildAt(0) val settings = itemView.requireViewById<View>(R.id.settings) init { diff --git a/packages/SystemUI/src/com/android/systemui/media/SeekBarViewModel.kt b/packages/SystemUI/src/com/android/systemui/media/SeekBarViewModel.kt index 9e326aaec3c1..c8244589ce44 100644 --- a/packages/SystemUI/src/com/android/systemui/media/SeekBarViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/media/SeekBarViewModel.kt @@ -91,9 +91,9 @@ class SeekBarViewModel @Inject constructor(@Background private val bgExecutor: R } private var playbackState: PlaybackState? = null private var callback = object : MediaController.Callback() { - override fun onPlaybackStateChanged(state: PlaybackState) { + override fun onPlaybackStateChanged(state: PlaybackState?) { playbackState = state - if (PlaybackState.STATE_NONE.equals(playbackState)) { + if (playbackState == null || PlaybackState.STATE_NONE.equals(playbackState)) { clearController() } else { checkIfPollingNeeded() diff --git a/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java b/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java new file mode 100644 index 000000000000..57ac9dfb52cd --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.media.dagger; + +import com.android.systemui.dagger.SysUISingleton; +import com.android.systemui.media.MediaDataManager; +import com.android.systemui.media.MediaHierarchyManager; +import com.android.systemui.media.MediaHost; +import com.android.systemui.media.MediaHostStatesManager; + +import javax.inject.Named; + +import dagger.Module; +import dagger.Provides; + +/** Dagger module for the media package. */ +@Module +public interface MediaModule { + String QS_PANEL = "media_qs_panel"; + String QUICK_QS_PANEL = "media_quick_qs_panel"; + String KEYGUARD = "media_keyguard"; + + /** */ + @Provides + @SysUISingleton + @Named(QS_PANEL) + static MediaHost providesQSMediaHost(MediaHost.MediaHostStateHolder stateHolder, + MediaHierarchyManager hierarchyManager, MediaDataManager dataManager, + MediaHostStatesManager statesManager) { + return new MediaHost(stateHolder, hierarchyManager, dataManager, statesManager); + } + + /** */ + @Provides + @SysUISingleton + @Named(QUICK_QS_PANEL) + static MediaHost providesQuickQSMediaHost(MediaHost.MediaHostStateHolder stateHolder, + MediaHierarchyManager hierarchyManager, MediaDataManager dataManager, + MediaHostStatesManager statesManager) { + return new MediaHost(stateHolder, hierarchyManager, dataManager, statesManager); + } + + /** */ + @Provides + @SysUISingleton + @Named(KEYGUARD) + static MediaHost providesKeyguardMediaHost(MediaHost.MediaHostStateHolder stateHolder, + MediaHierarchyManager hierarchyManager, MediaDataManager dataManager, + MediaHostStatesManager statesManager) { + return new MediaHost(stateHolder, hierarchyManager, dataManager, statesManager); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java index eeb93bb7d766..bdaeb137e368 100644 --- a/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java +++ b/packages/SystemUI/src/com/android/systemui/people/PeopleSpaceActivity.java @@ -18,6 +18,7 @@ package com.android.systemui.people; import android.app.Activity; import android.app.INotificationManager; +import android.app.people.ConversationChannel; import android.app.people.IPeopleManager; import android.content.Context; import android.content.pm.LauncherApps; @@ -29,6 +30,7 @@ import android.icu.util.MeasureUnit; import android.os.Bundle; import android.os.ServiceManager; import android.os.UserHandle; +import android.provider.Settings; import android.service.notification.ConversationChannelWrapper; import android.util.Log; import android.view.ViewGroup; @@ -52,7 +54,6 @@ public class PeopleSpaceActivity extends Activity { private INotificationManager mNotificationManager; private PackageManager mPackageManager; private LauncherApps mLauncherApps; - private List<ConversationChannelWrapper> mConversations; private Context mContext; @Override @@ -77,15 +78,25 @@ public class PeopleSpaceActivity extends Activity { */ private void setTileViewsWithPriorityConversations() { try { + boolean showAllConversations = Settings.Global.getInt(mContext.getContentResolver(), + Settings.Global.PEOPLE_SPACE_CONVERSATION_TYPE) == 0; List<ConversationChannelWrapper> conversations = mNotificationManager.getConversations( - true /* priority only */).getList(); - mConversations = conversations.stream().filter( - c -> shouldKeepConversation(c)).collect(Collectors.toList()); - for (ConversationChannelWrapper conversation : mConversations) { + !showAllConversations /* priority only */).getList(); + List<ShortcutInfo> shortcutInfos = conversations.stream().filter( + c -> shouldKeepConversation(c)).map(c -> c.getShortcutInfo()).collect( + Collectors.toList()); + if (showAllConversations) { + List<ConversationChannel> recentConversations = + mPeopleManager.getRecentConversations().getList(); + List<ShortcutInfo> recentShortcuts = recentConversations.stream().map( + c -> c.getShortcutInfo()).collect(Collectors.toList()); + shortcutInfos.addAll(recentShortcuts); + } + for (ShortcutInfo conversation : shortcutInfos) { PeopleSpaceTileView tileView = new PeopleSpaceTileView(mContext, mPeopleSpaceLayout, - conversation.getShortcutInfo().getId()); + conversation.getId()); setTileView(tileView, conversation); } } catch (Exception e) { @@ -95,11 +106,10 @@ public class PeopleSpaceActivity extends Activity { /** Sets {@code tileView} with the data in {@code conversation}. */ private void setTileView(PeopleSpaceTileView tileView, - ConversationChannelWrapper conversation) { + ShortcutInfo shortcutInfo) { try { - ShortcutInfo shortcutInfo = conversation.getShortcutInfo(); int userId = UserHandle.getUserHandleForUid( - conversation.getUid()).getIdentifier(); + shortcutInfo.getUserId()).getIdentifier(); String pkg = shortcutInfo.getPackage(); long lastInteraction = mPeopleManager.getLastInteraction( diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt index f56e6cdf5cb7..dc5ba693a658 100644 --- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt +++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt @@ -69,8 +69,10 @@ class PrivacyItemController @Inject constructor( private const val ALL_INDICATORS = SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED private const val MIC_CAMERA = SystemUiDeviceConfigFlags.PROPERTY_MIC_CAMERA_ENABLED + private const val LOCATION = SystemUiDeviceConfigFlags.PROPERTY_LOCATION_INDICATORS_ENABLED private const val DEFAULT_ALL_INDICATORS = false private const val DEFAULT_MIC_CAMERA = true + private const val DEFAULT_LOCATION = false } @VisibleForTesting @@ -88,6 +90,11 @@ class PrivacyItemController @Inject constructor( return true } + private fun isLocationEnabled(): Boolean { + return deviceConfigProxy.getBoolean(DeviceConfig.NAMESPACE_PRIVACY, + LOCATION, DEFAULT_LOCATION) + } + private var currentUserIds = emptyList<Int>() private var listening = false private val callbacks = mutableListOf<WeakReference<Callback>>() @@ -107,13 +114,15 @@ class PrivacyItemController @Inject constructor( private set var micCameraAvailable = isMicCameraEnabled() private set + var locationAvailable = isLocationEnabled() private val devicePropertiesChangedListener = object : DeviceConfig.OnPropertiesChangedListener { override fun onPropertiesChanged(properties: DeviceConfig.Properties) { if (DeviceConfig.NAMESPACE_PRIVACY.equals(properties.getNamespace()) && (properties.keyset.contains(ALL_INDICATORS) || - properties.keyset.contains(MIC_CAMERA))) { + properties.keyset.contains(MIC_CAMERA) || + properties.keyset.contains(LOCATION))) { // Running on the ui executor so can iterate on callbacks if (properties.keyset.contains(ALL_INDICATORS)) { @@ -126,6 +135,10 @@ class PrivacyItemController @Inject constructor( // micCameraAvailable = properties.getBoolean(MIC_CAMERA, DEFAULT_MIC_CAMERA) // callbacks.forEach { it.get()?.onFlagMicCameraChanged(micCameraAvailable) } // } + if (properties.keyset.contains(LOCATION)) { + locationAvailable = properties.getBoolean(LOCATION, DEFAULT_LOCATION) + callbacks.forEach { it.get()?.onFlagLocationChanged(locationAvailable) } + } internalUiExecutor.updateListeningState() } } @@ -139,7 +152,8 @@ class PrivacyItemController @Inject constructor( active: Boolean ) { // Check if we care about this code right now - if (!allIndicatorsAvailable && code in OPS_LOCATION) { + if (!allIndicatorsAvailable && + (code in OPS_LOCATION && !locationAvailable)) { return } val userId = UserHandle.getUserId(uid) @@ -195,7 +209,8 @@ class PrivacyItemController @Inject constructor( * main thread. */ private fun setListeningState() { - val listen = !callbacks.isEmpty() and (allIndicatorsAvailable || micCameraAvailable) + val listen = !callbacks.isEmpty() and + (allIndicatorsAvailable || micCameraAvailable || locationAvailable) if (listening == listen) return listening = listen if (listening) { @@ -258,7 +273,9 @@ class PrivacyItemController @Inject constructor( AppOpsManager.OP_RECORD_AUDIO -> PrivacyType.TYPE_MICROPHONE else -> return null } - if (type == PrivacyType.TYPE_LOCATION && !allIndicatorsAvailable) return null + if (type == PrivacyType.TYPE_LOCATION && (!allIndicatorsAvailable && !locationAvailable)) { + return null + } val app = PrivacyApplication(appOpItem.packageName, appOpItem.uid) return PrivacyItem(type, app) } @@ -271,6 +288,9 @@ class PrivacyItemController @Inject constructor( @JvmDefault fun onFlagMicCameraChanged(flag: Boolean) {} + + @JvmDefault + fun onFlagLocationChanged(flag: Boolean) {} } private class NotifyChangesToCallback( diff --git a/packages/SystemUI/src/com/android/systemui/qs/DoubleLineTileLayout.kt b/packages/SystemUI/src/com/android/systemui/qs/DoubleLineTileLayout.kt index dc157a8dd257..81076475c5ce 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/DoubleLineTileLayout.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/DoubleLineTileLayout.kt @@ -33,7 +33,7 @@ class DoubleLineTileLayout( private const val NUM_LINES = 2 } - protected val mRecords = ArrayList<QSPanel.TileRecord>() + protected val mRecords = ArrayList<QSPanelControllerBase.TileRecord>() private var _listening = false private var smallTileSize = 0 private val twoLineHeight @@ -50,17 +50,17 @@ class DoubleLineTileLayout( updateResources() } - override fun addTile(tile: QSPanel.TileRecord) { + override fun addTile(tile: QSPanelControllerBase.TileRecord) { mRecords.add(tile) tile.tile.setListening(this, _listening) addTileView(tile) } - protected fun addTileView(tile: QSPanel.TileRecord) { + protected fun addTileView(tile: QSPanelControllerBase.TileRecord) { addView(tile.tileView) } - override fun removeTile(tile: QSPanel.TileRecord) { + override fun removeTile(tile: QSPanelControllerBase.TileRecord) { mRecords.remove(tile) tile.tile.setListening(this, false) removeView(tile.tileView) @@ -72,7 +72,7 @@ class DoubleLineTileLayout( super.removeAllViews() } - override fun getOffsetTop(tile: QSPanel.TileRecord?) = top + override fun getOffsetTop(tile: QSPanelControllerBase.TileRecord?) = top override fun updateResources(): Boolean { with(mContext.resources) { diff --git a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java index 04f379ef35ea..3062a77bcbe1 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java +++ b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java @@ -26,7 +26,7 @@ import com.android.internal.logging.UiEventLogger; import com.android.systemui.R; import com.android.systemui.plugins.qs.QSTile; import com.android.systemui.qs.QSPanel.QSTileLayout; -import com.android.systemui.qs.QSPanel.TileRecord; +import com.android.systemui.qs.QSPanelControllerBase.TileRecord; import java.util.ArrayList; import java.util.Set; diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java index 9dcc924f161e..4d4195063227 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java @@ -28,12 +28,17 @@ import com.android.systemui.qs.QSHost.Callback; import com.android.systemui.qs.QSPanel.QSTileLayout; import com.android.systemui.qs.TouchAnimator.Builder; import com.android.systemui.qs.TouchAnimator.Listener; +import com.android.systemui.qs.dagger.QSScope; import com.android.systemui.tuner.TunerService; import com.android.systemui.tuner.TunerService.Tunable; import java.util.ArrayList; import java.util.Collection; +import javax.inject.Inject; + +/** */ +@QSScope public class QSAnimator implements Callback, PageListener, Listener, OnLayoutChangeListener, OnAttachStateChangeListener, Tunable { @@ -53,6 +58,9 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha private final ArrayList<View> mQuickQsViews = new ArrayList<>(); private final QuickQSPanel mQuickQsPanel; private final QSPanel mQsPanel; + private final QSPanelController mQsPanelController; + private final QuickQSPanelController mQuickQSPanelController; + private final QSSecurityFooter mSecurityFooter; private final QS mQs; private PagedTileLayout mPagedLayout; @@ -78,10 +86,19 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha private QSTileHost mHost; private boolean mShowCollapsedOnKeyguard; - public QSAnimator(QS qs, QuickQSPanel quickPanel, QSPanel panel) { + @Inject + public QSAnimator(QS qs, QuickQSPanel quickPanel, QSPanel panel, + QSPanelController qsPanelController, QuickQSPanelController quickQSPanelController, + QSTileHost qsTileHost, + QSSecurityFooter securityFooter) { mQs = qs; mQuickQsPanel = quickPanel; mQsPanel = panel; + mQsPanelController = qsPanelController; + mQuickQSPanelController = quickQSPanelController; + mSecurityFooter = securityFooter; + mHost = qsTileHost; + mHost.addCallback(this); mQsPanel.addOnAttachStateChangeListener(this); qs.getView().addOnLayoutChangeListener(this); if (mQsPanel.isAttachedToWindow()) { @@ -134,12 +151,6 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha && !mShowCollapsedOnKeyguard ? View.INVISIBLE : View.VISIBLE); } - public void setHost(QSTileHost qsh) { - mHost = qsh; - qsh.addCallback(this); - updateAnimators(); - } - @Override public void onViewAttachedToWindow(View v) { Dependency.get(TunerService.class).addTunable(this, ALLOW_FANCY_ANIMATION, @@ -148,9 +159,7 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha @Override public void onViewDetachedFromWindow(View v) { - if (mHost != null) { - mHost.removeCallback(this); - } + mHost.removeCallback(this); Dependency.get(TunerService.class).removeTunable(this); } @@ -185,8 +194,7 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha TouchAnimator.Builder translationXBuilder = new Builder(); TouchAnimator.Builder translationYBuilder = new Builder(); - if (mQsPanel.getHost() == null) return; - Collection<QSTile> tiles = mQsPanel.getHost().getTiles(); + Collection<QSTile> tiles = mHost.getTiles(); int count = 0; int[] loc1 = new int[2]; int[] loc2 = new int[2]; @@ -206,7 +214,7 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha firstPageBuilder.addFloat(tileLayout, "translationY", heightDiff, 0); for (QSTile tile : tiles) { - QSTileView tileView = mQsPanel.getTileView(tile); + QSTileView tileView = mQsPanelController.getTileView(tile); if (tileView == null) { Log.e(TAG, "tileView is null " + tile.getTileSpec()); continue; @@ -217,7 +225,7 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha // This case: less tiles to animate in small displays. if (count < mQuickQsPanel.getTileLayout().getNumVisibleTiles() && mAllowFancy) { // Quick tiles. - QSTileView quickTileView = mQuickQsPanel.getTileView(tile); + QSTileView quickTileView = mQuickQSPanelController.getTileView(tile); if (quickTileView == null) continue; lastX = loc1[0]; @@ -302,16 +310,12 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha // Fade in the security footer and the divider as we reach the final position builder = new Builder().setStartDelay(EXPANDED_TILE_DELAY); - if (mQsPanel.getSecurityFooter() != null) { - builder.addFloat(mQsPanel.getSecurityFooter().getView(), "alpha", 0, 1); - } + builder.addFloat(mSecurityFooter.getView(), "alpha", 0, 1); if (mQsPanel.getDivider() != null) { builder.addFloat(mQsPanel.getDivider(), "alpha", 0, 1); } mAllPagesDelayedAnimator = builder.build(); - if (mQsPanel.getSecurityFooter() != null) { - mAllViews.add(mQsPanel.getSecurityFooter().getView()); - } + mAllViews.add(mSecurityFooter.getView()); if (mQsPanel.getDivider() != null) { mAllViews.add(mQsPanel.getDivider()); } diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImplController.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImplController.java index 1e239b1e9ec9..acead987a06a 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImplController.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImplController.java @@ -16,19 +16,21 @@ package com.android.systemui.qs; -import com.android.systemui.R; +import com.android.systemui.qs.dagger.QSScope; import com.android.systemui.util.ViewController; import javax.inject.Inject; -class QSContainerImplController extends ViewController<QSContainerImpl> { +/** */ +@QSScope +public class QSContainerImplController extends ViewController<QSContainerImpl> { private final QuickStatusBarHeaderController mQuickStatusBarHeaderController; - private QSContainerImplController(QSContainerImpl view, - QuickStatusBarHeaderController.Builder quickStatusBarHeaderControllerBuilder) { + @Inject + QSContainerImplController(QSContainerImpl view, + QuickStatusBarHeaderController quickStatusBarHeaderController) { super(view); - mQuickStatusBarHeaderController = quickStatusBarHeaderControllerBuilder - .setQuickStatusBarHeader(mView.findViewById(R.id.header)).build(); + mQuickStatusBarHeaderController = quickStatusBarHeaderController; } @Override @@ -49,23 +51,7 @@ class QSContainerImplController extends ViewController<QSContainerImpl> { protected void onViewDetached() { } - static class Builder { - private final QuickStatusBarHeaderController.Builder mQuickStatusBarHeaderControllerBuilder; - private QSContainerImpl mView; - - @Inject - Builder( - QuickStatusBarHeaderController.Builder quickStatusBarHeaderControllerBuilder) { - mQuickStatusBarHeaderControllerBuilder = quickStatusBarHeaderControllerBuilder; - } - - public Builder setQSContainerImpl(QSContainerImpl view) { - mView = view; - return this; - } - - public QSContainerImplController build() { - return new QSContainerImplController(mView, mQuickStatusBarHeaderControllerBuilder); - } + public QSContainerImpl getView() { + return mView; } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java b/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java index 2be8a9704e1c..cfcceb2b2951 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSDetail.java @@ -76,7 +76,7 @@ public class QSDetail extends LinearLayout { private int mOpenY; private boolean mAnimatingOpen; private boolean mSwitchState; - private View mFooter; + private QSFooter mFooter; public QSDetail(Context context, @Nullable AttributeSet attrs) { super(context, attrs); @@ -120,7 +120,8 @@ public class QSDetail extends LinearLayout { mDetailDoneButton.setOnClickListener(doneListener); } - public void setQsPanel(QSPanel panel, QuickStatusBarHeader header, View footer) { + /** */ + public void setQsPanel(QSPanel panel, QuickStatusBarHeader header, QSFooter footer) { mQsPanel = panel; mHeader = header; mFooter = footer; diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooterView.java index 84563a078447..8b9dae14c809 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSFooterImpl.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooterView.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2017 The Android Open Source Project + * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -11,19 +11,14 @@ * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 com.android.systemui.qs; import static android.app.StatusBarManager.DISABLE2_QUICK_SETTINGS; -import static com.android.systemui.util.InjectionInflationController.VIEW_CONTEXT; - -import android.content.ClipData; -import android.content.ClipboardManager; import android.content.Context; -import android.content.Intent; import android.content.res.Configuration; import android.database.ContentObserver; import android.graphics.PorterDuff.Mode; @@ -36,50 +31,27 @@ import android.os.Handler; import android.os.UserHandle; import android.os.UserManager; import android.provider.Settings; -import android.text.TextUtils; import android.util.AttributeSet; import android.view.View; -import android.view.View.OnClickListener; import android.view.accessibility.AccessibilityNodeInfo; import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.TextView; -import android.widget.Toast; import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; -import com.android.internal.logging.MetricsLogger; -import com.android.internal.logging.nano.MetricsProto; -import com.android.keyguard.KeyguardUpdateMonitor; import com.android.settingslib.Utils; import com.android.settingslib.development.DevelopmentSettingsEnabler; import com.android.settingslib.drawable.UserIconDrawable; -import com.android.systemui.Dependency; import com.android.systemui.R; import com.android.systemui.R.dimen; -import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.qs.TouchAnimator.Builder; -import com.android.systemui.settings.UserTracker; import com.android.systemui.statusbar.phone.MultiUserSwitch; import com.android.systemui.statusbar.phone.SettingsButton; -import com.android.systemui.statusbar.policy.DeviceProvisionedController; -import com.android.systemui.statusbar.policy.UserInfoController; -import com.android.systemui.statusbar.policy.UserInfoController.OnUserInfoChangedListener; -import com.android.systemui.tuner.TunerService; - -import javax.inject.Inject; -import javax.inject.Named; - -public class QSFooterImpl extends FrameLayout implements QSFooter, - OnClickListener, OnUserInfoChangedListener { - private static final String TAG = "QSFooterImpl"; - - private final ActivityStarter mActivityStarter; - private final UserInfoController mUserInfoController; - private final DeviceProvisionedController mDeviceProvisionedController; - private final UserTracker mUserTracker; +/** */ +public class QSFooterView extends FrameLayout { private SettingsButton mSettingsButton; protected View mSettingsContainer; private PageIndicator mPageIndicator; @@ -87,7 +59,6 @@ public class QSFooterImpl extends FrameLayout implements QSFooter, private boolean mShouldShowBuildText; private boolean mQsDisabled; - private QSPanel mQsPanel; private QuickQSPanel mQuickQsPanel; private boolean mExpanded; @@ -117,39 +88,19 @@ public class QSFooterImpl extends FrameLayout implements QSFooter, } }; - @Inject - public QSFooterImpl(@Named(VIEW_CONTEXT) Context context, AttributeSet attrs, - ActivityStarter activityStarter, UserInfoController userInfoController, - DeviceProvisionedController deviceProvisionedController, UserTracker userTracker) { + public QSFooterView(Context context, AttributeSet attrs) { super(context, attrs); - mActivityStarter = activityStarter; - mUserInfoController = userInfoController; - mDeviceProvisionedController = deviceProvisionedController; - mUserTracker = userTracker; - } - - @VisibleForTesting - public QSFooterImpl(Context context, AttributeSet attrs) { - this(context, attrs, - Dependency.get(ActivityStarter.class), - Dependency.get(UserInfoController.class), - Dependency.get(DeviceProvisionedController.class), - Dependency.get(UserTracker.class)); } @Override protected void onFinishInflate() { super.onFinishInflate(); mEdit = findViewById(android.R.id.edit); - mEdit.setOnClickListener(view -> - mActivityStarter.postQSRunnableDismissingKeyguard(() -> - mQsPanel.showEdit(view))); mPageIndicator = findViewById(R.id.footer_page_indicator); mSettingsButton = findViewById(R.id.settings_button); mSettingsContainer = findViewById(R.id.settings_button_container); - mSettingsButton.setOnClickListener(this); mMultiUserSwitch = findViewById(R.id.multi_user_switch); mMultiUserAvatar = mMultiUserSwitch.findViewById(R.id.multi_user_avatar); @@ -157,19 +108,6 @@ public class QSFooterImpl extends FrameLayout implements QSFooter, mActionsContainer = findViewById(R.id.qs_footer_actions_container); mEditContainer = findViewById(R.id.qs_footer_actions_edit_container); mBuildText = findViewById(R.id.build); - mBuildText.setOnLongClickListener(view -> { - CharSequence buildText = mBuildText.getText(); - if (!TextUtils.isEmpty(buildText)) { - ClipboardManager service = - mUserTracker.getUserContext().getSystemService(ClipboardManager.class); - String label = mContext.getString(R.string.build_number_clip_data_label); - service.setPrimaryClip(ClipData.newPlainText(label, buildText)); - Toast.makeText(mContext, R.string.build_number_copy_toast, Toast.LENGTH_SHORT) - .show(); - return true; - } - return false; - }); // RenderThread is doing more harm than good when touching the header (to expand quick // settings), so disable it for this view @@ -180,7 +118,6 @@ public class QSFooterImpl extends FrameLayout implements QSFooter, addOnLayoutChangeListener((v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> updateAnimator(right - left)); setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES); - updateEverything(); setBuildText(); } @@ -249,24 +186,22 @@ public class QSFooterImpl extends FrameLayout implements QSFooter, .build(); } - @Override - public void setKeyguardShowing(boolean keyguardShowing) { + /** */ + public void setKeyguardShowing() { setExpansion(mExpansionAmount); } - @Override public void setExpandClickListener(OnClickListener onClickListener) { mExpandClickListener = onClickListener; } - @Override - public void setExpanded(boolean expanded) { + void setExpanded(boolean expanded, boolean isTunerEnabled) { if (mExpanded == expanded) return; mExpanded = expanded; - updateEverything(); + updateEverything(isTunerEnabled); } - @Override + /** */ public void setExpansion(float headerExpansionFraction) { mExpansionAmount = headerExpansionFraction; if (mSettingsCogAnimator != null) mSettingsCogAnimator.setPosition(headerExpansionFraction); @@ -287,18 +222,16 @@ public class QSFooterImpl extends FrameLayout implements QSFooter, @Override @VisibleForTesting public void onDetachedFromWindow() { - setListening(false); mContext.getContentResolver().unregisterContentObserver(mDeveloperSettingsObserver); super.onDetachedFromWindow(); } - @Override + /** */ public void setListening(boolean listening) { if (listening == mListening) { return; } mListening = listening; - updateListeners(); } @Override @@ -318,17 +251,16 @@ public class QSFooterImpl extends FrameLayout implements QSFooter, info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_EXPAND); } - @Override - public void disable(int state1, int state2, boolean animate) { + void disable(int state2, boolean isTunerEnabled) { final boolean disabled = (state2 & DISABLE2_QUICK_SETTINGS) != 0; if (disabled == mQsDisabled) return; mQsDisabled = disabled; - updateEverything(); + updateEverything(isTunerEnabled); } - public void updateEverything() { + void updateEverything(boolean isTunerEnabled) { post(() -> { - updateVisibilities(); + updateVisibilities(isTunerEnabled); updateClickabilities(); setClickable(false); }); @@ -341,11 +273,10 @@ public class QSFooterImpl extends FrameLayout implements QSFooter, mBuildText.setLongClickable(mBuildText.getVisibility() == View.VISIBLE); } - private void updateVisibilities() { + private void updateVisibilities(boolean isTunerEnabled) { mSettingsContainer.setVisibility(mQsDisabled ? View.GONE : View.VISIBLE); mSettingsContainer.findViewById(R.id.tuner_icon).setVisibility( - TunerService.isTunerEnabled(mContext, mUserTracker.getUserHandle()) ? View.VISIBLE - : View.INVISIBLE); + isTunerEnabled ? View.VISIBLE : View.INVISIBLE); final boolean isDemo = UserManager.isDeviceInDemoMode(mContext); mMultiUserSwitch.setVisibility(showUserSwitcher() ? View.VISIBLE : View.INVISIBLE); mEditContainer.setVisibility(isDemo || !mExpanded ? View.INVISIBLE : View.VISIBLE); @@ -358,78 +289,22 @@ public class QSFooterImpl extends FrameLayout implements QSFooter, return mExpanded && mMultiUserSwitch.isMultiUserEnabled(); } - private void updateListeners() { - if (mListening) { - mUserInfoController.addCallback(this); - } else { - mUserInfoController.removeCallback(this); - } - } - - @Override + /** */ public void setQSPanel(final QSPanel qsPanel) { - mQsPanel = qsPanel; - if (mQsPanel != null) { + if (qsPanel != null) { mMultiUserSwitch.setQsPanel(qsPanel); - mQsPanel.setFooterPageIndicator(mPageIndicator); + qsPanel.setFooterPageIndicator(mPageIndicator); } } - @Override public void setQQSPanel(@Nullable QuickQSPanel panel) { mQuickQsPanel = panel; } - @Override - public void onClick(View v) { - // Don't do anything until view are unhidden - if (!mExpanded) { - return; - } - - if (v == mSettingsButton) { - if (!mDeviceProvisionedController.isCurrentUserSetup()) { - // If user isn't setup just unlock the device and dump them back at SUW. - mActivityStarter.postQSRunnableDismissingKeyguard(() -> { - }); - return; - } - MetricsLogger.action(mContext, - mExpanded ? MetricsProto.MetricsEvent.ACTION_QS_EXPANDED_SETTINGS_LAUNCH - : MetricsProto.MetricsEvent.ACTION_QS_COLLAPSED_SETTINGS_LAUNCH); - if (mSettingsButton.isTunerClick()) { - mActivityStarter.postQSRunnableDismissingKeyguard(() -> { - if (TunerService.isTunerEnabled(mContext, mUserTracker.getUserHandle())) { - TunerService.showResetRequest(mContext, mUserTracker.getUserHandle(), - () -> { - // Relaunch settings so that the tuner disappears. - startSettingsActivity(); - }); - } else { - Toast.makeText(getContext(), R.string.tuner_toast, - Toast.LENGTH_LONG).show(); - TunerService.setTunerEnabled(mContext, mUserTracker.getUserHandle(), true); - } - startSettingsActivity(); - - }); - } else { - startSettingsActivity(); - } - } - } - - private void startSettingsActivity() { - mActivityStarter.startActivity(new Intent(android.provider.Settings.ACTION_SETTINGS), - true /* dismissShade */); - } - @Override - public void onUserInfoChanged(String name, Drawable picture, String userAccount) { - if (picture != null && - UserManager.get(mContext).isGuestUser(KeyguardUpdateMonitor.getCurrentUser()) && - !(picture instanceof UserIconDrawable)) { - picture = picture.getConstantState().newDrawable(mContext.getResources()).mutate(); + void onUserInfoChanged(Drawable picture, boolean isGuestUser) { + if (picture != null && isGuestUser && !(picture instanceof UserIconDrawable)) { + picture = picture.getConstantState().newDrawable(getResources()).mutate(); picture.setColorFilter( Utils.getColorAttrDefaultColor(mContext, android.R.attr.colorForeground), Mode.SRC_IN); diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java new file mode 100644 index 000000000000..e3af04bdc31e --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java @@ -0,0 +1,241 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs; + +import android.content.ClipData; +import android.content.ClipboardManager; +import android.content.Intent; +import android.graphics.drawable.Drawable; +import android.os.UserManager; +import android.text.TextUtils; +import android.view.View; +import android.widget.TextView; +import android.widget.Toast; + +import androidx.annotation.Nullable; + +import com.android.internal.logging.MetricsLogger; +import com.android.internal.logging.nano.MetricsProto; +import com.android.keyguard.KeyguardUpdateMonitor; +import com.android.systemui.R; +import com.android.systemui.plugins.ActivityStarter; +import com.android.systemui.qs.dagger.QSScope; +import com.android.systemui.settings.UserTracker; +import com.android.systemui.statusbar.phone.SettingsButton; +import com.android.systemui.statusbar.policy.DeviceProvisionedController; +import com.android.systemui.statusbar.policy.UserInfoController; +import com.android.systemui.tuner.TunerService; +import com.android.systemui.util.ViewController; + +import javax.inject.Inject; + +/** + * Controller for {@link QSFooterView}. + */ +@QSScope +public class QSFooterViewController extends ViewController<QSFooterView> implements QSFooter { + + private final UserManager mUserManager; + private final UserInfoController mUserInfoController; + private final ActivityStarter mActivityStarter; + private final DeviceProvisionedController mDeviceProvisionedController; + private final UserTracker mUserTracker; + private final QSPanelController mQsPanelController; + private final TunerService mTunerService; + private final MetricsLogger mMetricsLogger; + private final SettingsButton mSettingsButton; + private final TextView mBuildText; + private final View mEdit; + + private final UserInfoController.OnUserInfoChangedListener mOnUserInfoChangedListener = + new UserInfoController.OnUserInfoChangedListener() { + @Override + public void onUserInfoChanged(String name, Drawable picture, String userAccount) { + boolean isGuestUser = mUserManager.isGuestUser(KeyguardUpdateMonitor.getCurrentUser()); + mView.onUserInfoChanged(picture, isGuestUser); + } + }; + + private final View.OnClickListener mSettingsOnClickListener = new View.OnClickListener() { + @Override + public void onClick(View v) { + // Don't do anything until view are unhidden + if (!mExpanded) { + return; + } + + if (v == mSettingsButton) { + if (!mDeviceProvisionedController.isCurrentUserSetup()) { + // If user isn't setup just unlock the device and dump them back at SUW. + mActivityStarter.postQSRunnableDismissingKeyguard(() -> { + }); + return; + } + mMetricsLogger.action( + mExpanded ? MetricsProto.MetricsEvent.ACTION_QS_EXPANDED_SETTINGS_LAUNCH + : MetricsProto.MetricsEvent.ACTION_QS_COLLAPSED_SETTINGS_LAUNCH); + if (mSettingsButton.isTunerClick()) { + mActivityStarter.postQSRunnableDismissingKeyguard(() -> { + if (isTunerEnabled()) { + mTunerService.showResetRequest( + mUserTracker.getUserHandle(), + () -> { + // Relaunch settings so that the tuner disappears. + startSettingsActivity(); + }); + } else { + Toast.makeText(getContext(), R.string.tuner_toast, + Toast.LENGTH_LONG).show(); + mTunerService.setTunerEnabled(mUserTracker.getUserHandle(), true); + } + startSettingsActivity(); + + }); + } else { + startSettingsActivity(); + } + } + } + }; + + private boolean mListening; + private boolean mExpanded; + + @Inject + QSFooterViewController(QSFooterView view, UserManager userManager, + UserInfoController userInfoController, ActivityStarter activityStarter, + DeviceProvisionedController deviceProvisionedController, UserTracker userTracker, + QSPanelController qsPanelController, TunerService tunerService, + MetricsLogger metricsLogger) { + super(view); + mUserManager = userManager; + mUserInfoController = userInfoController; + mActivityStarter = activityStarter; + mDeviceProvisionedController = deviceProvisionedController; + mUserTracker = userTracker; + mQsPanelController = qsPanelController; + mTunerService = tunerService; + mMetricsLogger = metricsLogger; + + mSettingsButton = mView.findViewById(R.id.settings_button); + mBuildText = mView.findViewById(R.id.build); + mEdit = mView.findViewById(android.R.id.edit); + } + + + @Override + protected void onViewAttached() { + mSettingsButton.setOnClickListener(mSettingsOnClickListener); + mBuildText.setOnLongClickListener(view -> { + CharSequence buildText = mBuildText.getText(); + if (!TextUtils.isEmpty(buildText)) { + ClipboardManager service = + mUserTracker.getUserContext().getSystemService(ClipboardManager.class); + String label = getResources().getString(R.string.build_number_clip_data_label); + service.setPrimaryClip(ClipData.newPlainText(label, buildText)); + Toast.makeText(getContext(), R.string.build_number_copy_toast, Toast.LENGTH_SHORT) + .show(); + return true; + } + return false; + }); + + mEdit.setOnClickListener(view -> + mActivityStarter.postQSRunnableDismissingKeyguard(() -> + mQsPanelController.showEdit(view))); + + mView.updateEverything(isTunerEnabled()); + } + + @Override + protected void onViewDetached() { + setListening(false); + } + + + @Override + public void setQSPanel(@Nullable QSPanel panel) { + mView.setQSPanel(panel); + } + + @Override + public void setVisibility(int visibility) { + mView.setVisibility(visibility); + } + + @Override + public void setExpanded(boolean expanded) { + mExpanded = expanded; + mView.setExpanded(expanded, isTunerEnabled()); + } + + + @Override + public int getHeight() { + return mView.getHeight(); + } + + @Override + public void setExpansion(float expansion) { + mView.setExpansion(expansion); + } + + @Override + public void setListening(boolean listening) { + if (mListening == listening) { + return; + } + + mListening = listening; + if (mListening) { + mUserInfoController.addCallback(mOnUserInfoChangedListener); + } else { + mUserInfoController.removeCallback(mOnUserInfoChangedListener); + } + } + + @Override + public void setKeyguardShowing(boolean keyguardShowing) { + mView.setKeyguardShowing(); + } + + /** */ + @Override + public void setExpandClickListener(View.OnClickListener onClickListener) { + mView.setExpandClickListener(onClickListener); + } + + @Override + public void setQQSPanel(@Nullable QuickQSPanel panel) { + mView.setQQSPanel(panel); + } + + @Override + public void disable(int state1, int state2, boolean animate) { + mView.disable(state2, isTunerEnabled()); + } + + + private void startSettingsActivity() { + mActivityStarter.startActivity(new Intent(android.provider.Settings.ACTION_SETTINGS), + true /* dismissShade */); + } + + private boolean isTunerEnabled() { + return mTunerService.isTunerEnabled(mUserTracker.getUserHandle()); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java index 3a783653a2d8..1a7d366d84b4 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java @@ -35,11 +35,11 @@ import androidx.annotation.VisibleForTesting; import com.android.systemui.Interpolators; import com.android.systemui.R; -import com.android.systemui.R.id; import com.android.systemui.media.MediaHost; import com.android.systemui.plugins.qs.QS; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.qs.customize.QSCustomizer; +import com.android.systemui.qs.dagger.QSFragmentComponent; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.notification.stack.StackStateAnimator; @@ -70,7 +70,6 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca private HeightListener mPanelView; protected QuickStatusBarHeader mHeader; private QSCustomizer mQSCustomizer; - protected QSPanel mQSPanel; protected NonInterceptingScrollView mQSPanelScrollView; private QSDetail mQSDetail; private boolean mListening; @@ -82,7 +81,7 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca private final RemoteInputQuickSettingsDisabler mRemoteInputQuickSettingsDisabler; private final InjectionInflationController mInjectionInflater; - private final QSContainerImplController.Builder mQSContainerImplControllerBuilder; + private final QSFragmentComponent.Factory mQsComponentFactory; private final QSTileHost mHost; private boolean mShowCollapsedOnKeyguard; private boolean mLastKeyguardAndExpanded; @@ -96,15 +95,17 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca private int[] mTmpLocation = new int[2]; private int mLastViewHeight; private float mLastHeaderTranslation; + private QSPanelController mQSPanelController; + private QuickQSPanelController mQuickQSPanelController; @Inject public QSFragment(RemoteInputQuickSettingsDisabler remoteInputQsDisabler, InjectionInflationController injectionInflater, QSTileHost qsTileHost, StatusBarStateController statusBarStateController, CommandQueue commandQueue, - QSContainerImplController.Builder qsContainerImplControllerBuilder) { + QSFragmentComponent.Factory qsComponentFactory) { mRemoteInputQuickSettingsDisabler = remoteInputQsDisabler; mInjectionInflater = injectionInflater; - mQSContainerImplControllerBuilder = qsContainerImplControllerBuilder; + mQsComponentFactory = qsComponentFactory; commandQueue.observe(getLifecycle(), this); mHost = qsTileHost; mStatusBarStateController = statusBarStateController; @@ -120,8 +121,13 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca @Override public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); - mQSPanel = view.findViewById(R.id.quick_settings_panel); + QSFragmentComponent qsFragmentComponent = mQsComponentFactory.create(this); + mQSPanelController = qsFragmentComponent.getQSPanelController(); + mQuickQSPanelController = qsFragmentComponent.getQuickQSPanelController(); + + mQSPanelController.init(); + mQuickQSPanelController.init(); + mQSPanelScrollView = view.findViewById(R.id.expanded_qs_scroll_view); mQSPanelScrollView.addOnLayoutChangeListener( (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> { @@ -135,18 +141,15 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca }); mQSDetail = view.findViewById(R.id.qs_detail); mHeader = view.findViewById(R.id.header); - mQSPanel.setHeaderContainer(view.findViewById(R.id.header_text_container)); - mFooter = view.findViewById(R.id.qs_footer); - mContainer = view.findViewById(id.quick_settings_container); + mQSPanelController.setHeaderContainer(view.findViewById(R.id.header_text_container)); + mFooter = qsFragmentComponent.getQSFooter(); - mQSContainerImplController = mQSContainerImplControllerBuilder - .setQSContainerImpl((QSContainerImpl) view) - .build(); + mQSContainerImplController = qsFragmentComponent.getQSContainerImplController(); mQSContainerImplController.init(); + mContainer = mQSContainerImplController.getView(); - mQSDetail.setQsPanel(mQSPanel, mHeader, (View) mFooter); - mQSAnimator = new QSAnimator(this, mHeader.findViewById(R.id.quick_qs_panel), mQSPanel); - + mQSDetail.setQsPanel(mQSPanelController.getView(), mHeader, mFooter); + mQSAnimator = qsFragmentComponent.getQSAnimator(); mQSCustomizer = view.findViewById(R.id.qs_customize); mQSCustomizer.setQs(this); @@ -156,7 +159,7 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca setEditLocation(view); mQSCustomizer.restoreInstanceState(savedInstanceState); if (mQsExpanded) { - mQSPanel.getTileLayout().restoreInstanceState(savedInstanceState); + mQSPanelController.getTileLayout().restoreInstanceState(savedInstanceState); } } setHost(mHost); @@ -188,7 +191,7 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca outState.putBoolean(EXTRA_LISTENING, mListening); mQSCustomizer.saveInstanceState(outState); if (mQsExpanded) { - mQSPanel.getTileLayout().saveInstanceState(outState); + mQSPanelController.getTileLayout().saveInstanceState(outState); } } @@ -249,14 +252,10 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca } public void setHost(QSTileHost qsh) { - mQSPanel.setHost(qsh, mQSCustomizer); - mHeader.setQSPanel(mQSPanel); - mFooter.setQSPanel(mQSPanel); + mQSPanelController.setCustomizer(mQSCustomizer); + mHeader.setQSPanel(mQSPanelController.getView()); + mFooter.setQSPanel(mQSPanelController.getView()); mQSDetail.setHost(qsh); - - if (mQSAnimator != null) { - mQSAnimator.setHost(qsh); - } } @Override @@ -278,7 +277,7 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca private void updateQsState() { final boolean expandVisually = mQsExpanded || mStackScrollerOverscrolling || mHeaderAnimating; - mQSPanel.setExpanded(mQsExpanded); + mQSPanelController.setExpanded(mQsExpanded); mQSDetail.setExpanded(mQsExpanded); boolean keyguardShowing = isKeyguardShowing(); mHeader.setVisibility((mQsExpanded || !keyguardShowing || mHeaderAnimating @@ -294,7 +293,8 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca : View.INVISIBLE); mFooter.setExpanded((keyguardShowing && !mHeaderAnimating && !mShowCollapsedOnKeyguard) || (mQsExpanded && !mStackScrollerOverscrolling)); - mQSPanel.setVisibility(!mQsDisabled && expandVisually ? View.VISIBLE : View.INVISIBLE); + mQSPanelController.setVisibility( + !mQsDisabled && expandVisually ? View.VISIBLE : View.INVISIBLE); } private boolean isKeyguardShowing() { @@ -317,8 +317,12 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca } } + public QSPanelController getQSPanelController() { + return mQSPanelController; + } + public QSPanel getQsPanel() { - return mQSPanel; + return mQSPanelController.getView(); } public QSCustomizer getCustomizer() { @@ -327,7 +331,7 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca @Override public boolean isShowingDetail() { - return mQSPanel.isShowingCustomize() || mQSDetail.isShowingDetail(); + return mQSPanelController.isShowingCustomize() || mQSDetail.isShowingDetail(); } @Override @@ -339,7 +343,7 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca public void setExpanded(boolean expanded) { if (DEBUG) Log.d(TAG, "setExpanded " + expanded); mQsExpanded = expanded; - mQSPanel.setListening(mListening, mQsExpanded); + mQSPanelController.setListening(mListening, mQsExpanded); updateQsState(); } @@ -368,7 +372,7 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca mListening = listening; mQSContainerImplController.setListening(listening); mFooter.setListening(listening); - mQSPanel.setListening(mListening, mQsExpanded); + mQSPanelController.setListening(mListening, mQsExpanded); } @Override @@ -406,11 +410,15 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca float panelTranslationY = translationScaleY * heightDiff; // Let the views animate their contents correctly by giving them the necessary context. - mHeader.setExpansion(onKeyguardAndExpanded, expansion, - panelTranslationY); + mHeader.setExpansion(onKeyguardAndExpanded, expansion, panelTranslationY); + if (expansion < 1 && expansion > 0.99) { + if (mQuickQSPanelController.switchTileLayout(false)) { + mHeader.updateResources(); + } + } mFooter.setExpansion(onKeyguardAndExpanded ? 1 : expansion); - mQSPanel.getQsTileRevealController().setExpansion(expansion); - mQSPanel.getTileLayout().setExpansion(expansion); + mQSPanelController.getQsTileRevealController().setExpansion(expansion); + mQSPanelController.getTileLayout().setExpansion(expansion); mQSPanelScrollView.setTranslationY(translationScaleY * heightDiff); if (fullyCollapsed) { mQSPanelScrollView.setScrollY(0); @@ -448,7 +456,8 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca float expandedMediaPosition = absoluteBottomPosition - mQSPanelScrollView.getScrollY() + mQSPanelScrollView.getScrollRange(); // The expanded media host should never move below the laid out position - pinToBottom(expandedMediaPosition, mQSPanel.getMediaHost(), true /* expanded */); + pinToBottom( + expandedMediaPosition, mQSPanelController.getMediaHost(), true /* expanded */); // The expanded media host should never move above the laid out position pinToBottom(absoluteBottomPosition, mHeader.getHeaderQsPanel().getMediaHost(), false /* expanded */); @@ -538,7 +547,7 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca @Override public void closeDetail() { - mQSPanel.closeDetail(); + mQSPanelController.closeDetail(); } public void notifyCustomizeChanged() { @@ -553,8 +562,8 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca } /** - * The height this view wants to be. This is different from {@link #getMeasuredHeight} such that - * during closing the detail panel, this already returns the smaller height. + * The height this view wants to be. This is different from {@link View#getMeasuredHeight} such + * that during closing the detail panel, this already returns the smaller height. */ @Override public int getDesiredHeight() { diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java index ca3e4cfbe421..1b17a2a277f2 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java @@ -16,16 +16,15 @@ package com.android.systemui.qs; +import static com.android.systemui.media.dagger.MediaModule.QS_PANEL; import static com.android.systemui.util.InjectionInflationController.VIEW_CONTEXT; import static com.android.systemui.util.Utils.useQsMediaPlayer; import android.annotation.NonNull; import android.annotation.Nullable; -import android.content.ComponentName; import android.content.Context; import android.content.res.Configuration; import android.content.res.Resources; -import android.metrics.LogMaker; import android.os.Bundle; import android.os.Handler; import android.os.Message; @@ -42,41 +41,30 @@ import com.android.internal.logging.UiEventLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.widget.RemeasuringLinearLayout; import com.android.systemui.Dependency; -import com.android.systemui.Dumpable; import com.android.systemui.R; -import com.android.systemui.broadcast.BroadcastDispatcher; -import com.android.systemui.dump.DumpManager; import com.android.systemui.media.MediaHierarchyManager; import com.android.systemui.media.MediaHost; import com.android.systemui.plugins.qs.DetailAdapter; import com.android.systemui.plugins.qs.QSTile; -import com.android.systemui.plugins.qs.QSTileView; -import com.android.systemui.qs.QSHost.Callback; import com.android.systemui.qs.customize.QSCustomizer; -import com.android.systemui.qs.external.CustomTile; import com.android.systemui.qs.logging.QSLogger; -import com.android.systemui.settings.BrightnessController; import com.android.systemui.settings.ToggleSliderView; -import com.android.systemui.settings.UserTracker; import com.android.systemui.statusbar.policy.BrightnessMirrorController; import com.android.systemui.statusbar.policy.BrightnessMirrorController.BrightnessMirrorListener; import com.android.systemui.tuner.TunerService; import com.android.systemui.tuner.TunerService.Tunable; import com.android.systemui.util.animation.DisappearParameters; -import java.io.FileDescriptor; -import java.io.PrintWriter; import java.util.ArrayList; import java.util.Collection; +import java.util.List; import java.util.function.Consumer; -import java.util.stream.Collectors; import javax.inject.Inject; import javax.inject.Named; /** View that represents the quick settings tile panel (when expanded/pulled down). **/ -public class QSPanel extends LinearLayout implements Tunable, Callback, BrightnessMirrorListener, - Dumpable { +public class QSPanel extends LinearLayout implements Tunable, BrightnessMirrorListener { public static final String QS_SHOW_BRIGHTNESS = "qs_show_brightness"; public static final String QS_SHOW_HEADER = "qs_show_header"; @@ -84,20 +72,15 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne private static final String TAG = "QSPanel"; protected final Context mContext; - protected final ArrayList<TileRecord> mRecords = new ArrayList<>(); - private final BroadcastDispatcher mBroadcastDispatcher; protected final MediaHost mMediaHost; /** * The index where the content starts that needs to be moved between parents */ private final int mMovableContentStartIndex; - private String mCachedSpecs = ""; @Nullable protected View mBrightnessView; - @Nullable - private BrightnessController mBrightnessController; private final H mHandler = new H(); private final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class); @@ -111,14 +94,14 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne protected boolean mListening; private QSDetail.Callback mCallback; - private final DumpManager mDumpManager; private final QSLogger mQSLogger; protected final UiEventLogger mUiEventLogger; protected QSTileHost mHost; - private final UserTracker mUserTracker; + private final List<OnConfigurationChangedListener> mOnConfigurationChangedListeners = + new ArrayList<>(); @Nullable - protected QSSecurityFooter mSecurityFooter; + protected View mSecurityFooter; @Nullable protected View mFooter; @@ -155,12 +138,9 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne public QSPanel( @Named(VIEW_CONTEXT) Context context, AttributeSet attrs, - DumpManager dumpManager, - BroadcastDispatcher broadcastDispatcher, QSLogger qsLogger, - MediaHost mediaHost, - UiEventLogger uiEventLogger, - UserTracker userTracker + @Named(QS_PANEL) MediaHost mediaHost, + UiEventLogger uiEventLogger ) { super(context, attrs); mUsingMediaPlayer = useQsMediaPlayer(context); @@ -173,16 +153,14 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne }); mContext = context; mQSLogger = qsLogger; - mDumpManager = dumpManager; - mBroadcastDispatcher = broadcastDispatcher; mUiEventLogger = uiEventLogger; - mUserTracker = userTracker; setOrientation(VERTICAL); addViewsAboveTiles(); mMovableContentStartIndex = getChildCount(); mRegularTileLayout = createRegularTileLayout(); + mTileLayout = mRegularTileLayout; if (mUsingMediaPlayer) { mHorizontalLinearLayout = new RemeasuringLinearLayout(mContext); @@ -208,35 +186,27 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne initMediaHostState(); } - addSecurityFooter(); if (mRegularTileLayout instanceof PagedTileLayout) { mQsTileRevealController = new QSTileRevealController(mContext, this, (PagedTileLayout) mRegularTileLayout); } - mQSLogger.logAllTilesChangeListening(mListening, getDumpableTag(), mCachedSpecs); - updateResources(); + mQSLogger.logAllTilesChangeListening(mListening, getDumpableTag(), ""); } protected void onMediaVisibilityChanged(Boolean visible) { - switchTileLayout(); if (mMediaVisibilityChangedListener != null) { mMediaVisibilityChangedListener.accept(visible); } } - protected void addSecurityFooter() { - mSecurityFooter = new QSSecurityFooter(this, mContext, mUserTracker); - } - protected void addViewsAboveTiles() { mBrightnessView = LayoutInflater.from(mContext).inflate( R.layout.quick_settings_brightness_dialog, this, false); addView(mBrightnessView); - mBrightnessController = new BrightnessController(getContext(), - findViewById(R.id.brightness_slider), mBroadcastDispatcher); } - protected QSTileLayout createRegularTileLayout() { + /** */ + public QSTileLayout createRegularTileLayout() { if (mRegularTileLayout == null) { mRegularTileLayout = (QSTileLayout) LayoutInflater.from(mContext).inflate( R.layout.qs_paged_tile_layout, this, false); @@ -336,37 +306,10 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne } @Override - protected void onAttachedToWindow() { - super.onAttachedToWindow(); - final TunerService tunerService = Dependency.get(TunerService.class); - tunerService.addTunable(this, QS_SHOW_BRIGHTNESS); - - if (mHost != null) { - setTiles(mHost.getTiles()); - } - if (mBrightnessMirrorController != null) { - mBrightnessMirrorController.addCallback(this); - } - mDumpManager.registerDumpable(getDumpableTag(), this); - } - - @Override protected void onDetachedFromWindow() { - Dependency.get(TunerService.class).removeTunable(this); - if (mHost != null) { - mHost.removeCallback(this); - } if (mTileLayout != null) { mTileLayout.setListening(false); } - for (TileRecord record : mRecords) { - record.tile.removeCallbacks(); - } - mRecords.clear(); - if (mBrightnessMirrorController != null) { - mBrightnessMirrorController.removeCallback(this); - } - mDumpManager.unregisterDumpable(getDumpableTag()); super.onDetachedFromWindow(); } @@ -375,11 +318,6 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne } @Override - public void onTilesChanged() { - setTiles(mHost.getTiles()); - } - - @Override public void onTuningChanged(String key, String newValue) { if (QS_SHOW_BRIGHTNESS.equals(key) && mBrightnessView != null) { updateViewVisibilityForTuningValue(mBrightnessView, newValue); @@ -390,8 +328,8 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne view.setVisibility(TunerService.parseIntegerSwitch(newValue, true) ? VISIBLE : GONE); } - public void openDetails(String subPanel) { - QSTile tile = getTile(subPanel); + /** */ + public void openDetails(QSTile tile) { // If there's no tile with that name (as defined in QSFactoryImpl or other QSFactory), // QSFactory will not be able to create a tile and getTile will return null if (tile != null) { @@ -399,15 +337,6 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne } } - private QSTile getTile(String subPanel) { - for (int i = 0; i < mRecords.size(); i++) { - if (subPanel.equals(mRecords.get(i).tile.getTileSpec())) { - return mRecords.get(i).tile; - } - } - return mHost.createTile(subPanel); - } - public void setBrightnessMirror(BrightnessMirrorController c) { if (mBrightnessMirrorController != null) { mBrightnessMirrorController.removeCallback(this); @@ -433,17 +362,8 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne mCallback = callback; } - public void setHost(QSTileHost host, QSCustomizer customizer) { - mHost = host; - mHost.addCallback(this); - setTiles(mHost.getTiles()); - if (mSecurityFooter != null) { - mSecurityFooter.setHostEnvironment(host); - } + void setCustomizer(QSCustomizer customizer) { mCustomizePanel = customizer; - if (mCustomizePanel != null) { - mCustomizePanel.setHost(mHost); - } } /** @@ -482,9 +402,6 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne updatePageIndicator(); - if (mListening) { - refreshAllTiles(); - } if (mTileLayout != null) { mTileLayout.updateResources(); } @@ -505,20 +422,21 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne res.getDimensionPixelSize(R.dimen.qs_panel_padding_bottom)); } + void addOnConfigurationChangedListener(OnConfigurationChangedListener listener) { + mOnConfigurationChangedListeners.add(listener); + } + + void removeOnConfigurationChangedListener(OnConfigurationChangedListener listener) { + mOnConfigurationChangedListeners.remove(listener); + } + @Override protected void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); - if (mSecurityFooter != null) { - mSecurityFooter.onConfigurationChanged(); - } - updateResources(); + mOnConfigurationChangedListeners.forEach( + listener -> listener.onConfigurationChange(newConfig)); updateBrightnessMirror(); - - if (newConfig.orientation != mLastOrientation) { - mLastOrientation = newConfig.orientation; - switchTileLayout(); - } } @Override @@ -526,14 +444,9 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne super.onFinishInflate(); mFooter = findViewById(R.id.qs_footer); mDivider = findViewById(R.id.divider); - switchTileLayout(true /* force */); - } - - boolean switchTileLayout() { - return switchTileLayout(false /* force */); } - private boolean switchTileLayout(boolean force) { + boolean switchTileLayout(boolean force, List<QSPanelControllerBase.TileRecord> records) { /** Whether or not the QuickQSPanel currently contains a media player. */ boolean horizontal = shouldUseHorizontalLayout(); if (mDivider != null) { @@ -561,13 +474,12 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne reAttachMediaHost(); if (mTileLayout != null) { mTileLayout.setListening(false); - for (TileRecord record : mRecords) { + for (QSPanelControllerBase.TileRecord record : records) { mTileLayout.removeTile(record); record.tile.removeCallback(record.callback); } } mTileLayout = newLayout; - if (mHost != null) setTiles(mHost.getTiles()); newLayout.setListening(mListening); if (needsDynamicRowsAndColumns()) { newLayout.setMinRows(horizontal ? 2 : 1); @@ -616,20 +528,20 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne index++; if (mSecurityFooter != null) { - View view = mSecurityFooter.getView(); - LinearLayout.LayoutParams layoutParams = (LayoutParams) view.getLayoutParams(); + LinearLayout.LayoutParams layoutParams = + (LayoutParams) mSecurityFooter.getLayoutParams(); if (mUsingHorizontalLayout && mHeaderContainer != null) { // Adding the security view to the header, that enables us to avoid scrolling layoutParams.width = 0; layoutParams.weight = 1.6f; - switchToParent(view, mHeaderContainer, 1 /* always in second place */); + switchToParent(mSecurityFooter, mHeaderContainer, 1 /* always in second place */); } else { layoutParams.width = LayoutParams.WRAP_CONTENT; layoutParams.weight = 0; - switchToParent(view, parent, index); + switchToParent(mSecurityFooter, parent, index); index++; } - view.setLayoutParams(layoutParams); + mSecurityFooter.setLayoutParams(layoutParams); } if (mFooter != null) { @@ -702,14 +614,6 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne if (!mExpanded && mTileLayout instanceof PagedTileLayout) { ((PagedTileLayout) mTileLayout).setCurrentItem(0, false); } - mMetricsLogger.visibility(MetricsEvent.QS_PANEL, mExpanded); - if (!mExpanded) { - mUiEventLogger.log(closePanelEvent()); - closeDetail(); - } else { - mUiEventLogger.log(openPanelEvent()); - logTiles(); - } } public void setPageListener(final PagedTileLayout.PageListener pageListener) { @@ -722,56 +626,16 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne return mExpanded; } - public void setListening(boolean listening) { + /** */ + public void setListening(boolean listening, String cachedSpecs) { if (mListening == listening) return; mListening = listening; if (mTileLayout != null) { - mQSLogger.logAllTilesChangeListening(listening, getDumpableTag(), mCachedSpecs); + mQSLogger.logAllTilesChangeListening(listening, getDumpableTag(), cachedSpecs); mTileLayout.setListening(listening); } - if (mListening) { - refreshAllTiles(); - } } - private String getTilesSpecs() { - return mRecords.stream() - .map(tileRecord -> tileRecord.tile.getTileSpec()) - .collect(Collectors.joining(",")); - } - - public void setListening(boolean listening, boolean expanded) { - setListening(listening && expanded); - if (mSecurityFooter != null) { - mSecurityFooter.setListening(listening); - } - // Set the listening as soon as the QS fragment starts listening regardless of the expansion, - // so it will update the current brightness before the slider is visible. - setBrightnessListening(listening); - } - - public void setBrightnessListening(boolean listening) { - if (mBrightnessController == null) { - return; - } - if (listening) { - mBrightnessController.registerCallbacks(); - } else { - mBrightnessController.unregisterCallbacks(); - } - } - - public void refreshAllTiles() { - if (mBrightnessController != null) { - mBrightnessController.checkRestrictionAndSetEnabled(); - } - for (TileRecord r : mRecords) { - r.tile.refreshState(); - } - if (mSecurityFooter != null) { - mSecurityFooter.refreshState(); - } - } public void showDetailAdapter(boolean show, DetailAdapter adapter, int[] locationInWindow) { int xInWindow = locationInWindow[0]; @@ -793,33 +657,10 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne mHandler.obtainMessage(H.SHOW_DETAIL, show ? 1 : 0, 0, r).sendToTarget(); } - public void setTiles(Collection<QSTile> tiles) { - setTiles(tiles, false); - } - - public void setTiles(Collection<QSTile> tiles, boolean collapsedView) { - if (!collapsedView) { - mQsTileRevealController.updateRevealedTiles(tiles); - } - for (TileRecord record : mRecords) { - mTileLayout.removeTile(record); - record.tile.removeCallback(record.callback); - } - mRecords.clear(); - mCachedSpecs = ""; - for (QSTile tile : tiles) { - addTile(tile, collapsedView); - } - } - - protected void drawTile(TileRecord r, QSTile.State state) { + protected void drawTile(QSPanelControllerBase.TileRecord r, QSTile.State state) { r.tileView.onStateChanged(state); } - protected QSTileView createTileView(QSTile tile, boolean collapsedView) { - return mHost.createTileView(tile, collapsedView); - } - protected QSEvent openPanelEvent() { return QSEvent.QS_PANEL_EXPANDED; } @@ -836,14 +677,15 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne return mExpanded; } - protected TileRecord addTile(final QSTile tile, boolean collapsedView) { - final TileRecord r = new TileRecord(); - r.tile = tile; - r.tileView = createTileView(tile, collapsedView); + void updateRevealedTiles(Collection<QSTile> tiles) { + mQsTileRevealController.updateRevealedTiles(tiles); + } + + void addTile(QSPanelControllerBase.TileRecord tileRecord) { final QSTile.Callback callback = new QSTile.Callback() { @Override public void onStateChanged(QSTile.State state) { - drawTile(r, state); + drawTile(tileRecord, state); } @Override @@ -851,22 +693,22 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne // Both the collapsed and full QS panels get this callback, this check determines // which one should handle showing the detail. if (shouldShowDetail()) { - QSPanel.this.showDetail(show, r); + QSPanel.this.showDetail(show, tileRecord); } } @Override public void onToggleStateChanged(boolean state) { - if (mDetailRecord == r) { + if (mDetailRecord == tileRecord) { fireToggleStateChanged(state); } } @Override public void onScanStateChanged(boolean state) { - r.scanState = state; - if (mDetailRecord == r) { - fireScanStateChanged(r.scanState); + tileRecord.scanState = state; + if (mDetailRecord == tileRecord) { + fireScanStateChanged(tileRecord.scanState); } } @@ -878,20 +720,20 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne } } }; - r.tile.addCallback(callback); - r.callback = callback; - r.tileView.init(r.tile); - r.tile.refreshState(); - mRecords.add(r); - mCachedSpecs = getTilesSpecs(); + + tileRecord.tile.addCallback(callback); + tileRecord.callback = callback; + tileRecord.tileView.init(tileRecord.tile); + tileRecord.tile.refreshState(); if (mTileLayout != null) { - mTileLayout.addTile(r); + mTileLayout.addTile(tileRecord); } - - return r; } + void removeTile(QSPanelControllerBase.TileRecord tileRecord) { + mTileLayout.removeTile(tileRecord); + } public void showEdit(final View v) { v.post(new Runnable() { @@ -924,8 +766,8 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne } protected void handleShowDetail(Record r, boolean show) { - if (r instanceof TileRecord) { - handleShowDetailTile((TileRecord) r, show); + if (r instanceof QSPanelControllerBase.TileRecord) { + handleShowDetailTile((QSPanelControllerBase.TileRecord) r, show); } else { int x = 0; int y = 0; @@ -937,7 +779,7 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne } } - private void handleShowDetailTile(TileRecord r, boolean show) { + private void handleShowDetailTile(QSPanelControllerBase.TileRecord r, boolean show) { if ((mDetailRecord != null) == show && mDetailRecord == r) return; if (show) { @@ -958,8 +800,8 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne protected void setDetailRecord(Record r) { if (r == mDetailRecord) return; mDetailRecord = r; - final boolean scanState = mDetailRecord instanceof TileRecord - && ((TileRecord) mDetailRecord).scanState; + final boolean scanState = mDetailRecord instanceof QSPanelControllerBase.TileRecord + && ((QSPanelControllerBase.TileRecord) mDetailRecord).scanState; fireScanStateChanged(scanState); } @@ -971,15 +813,6 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne } mGridContentVisible = visible; } - - private void logTiles() { - for (int i = 0; i < mRecords.size(); i++) { - QSTile tile = mRecords.get(i).tile; - mMetricsLogger.write(tile.populate(new LogMaker(tile.getMetricsCategory()) - .setType(MetricsEvent.TYPE_OPEN))); - } - } - private void fireShowingDetail(DetailAdapter detail, int x, int y) { if (mCallback != null) { mCallback.onShowingDetail(detail, x, y); @@ -998,46 +831,15 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne } } - public void clickTile(ComponentName tile) { - final String spec = CustomTile.toSpec(tile); - final int N = mRecords.size(); - for (int i = 0; i < N; i++) { - if (mRecords.get(i).tile.getTileSpec().equals(spec)) { - mRecords.get(i).tile.click(); - break; - } - } - } - QSTileLayout getTileLayout() { return mTileLayout; } - QSTileView getTileView(QSTile tile) { - for (TileRecord r : mRecords) { - if (r.tile == tile) { - return r.tileView; - } - } - return null; - } - - @Nullable - public QSSecurityFooter getSecurityFooter() { - return mSecurityFooter; - } - @Nullable public View getDivider() { return mDivider; } - public void showDeviceMonitoringDialog() { - if (mSecurityFooter != null) { - mSecurityFooter.showDeviceMonitoringDialog(); - } - } - public void setContentMargins(int startMargin, int endMargin) { // Only some views actually want this content padding, others want to go all the way // to the edge like the brightness slider @@ -1120,9 +922,11 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne */ protected void updateMargins(View view, int start, int end) { LayoutParams lp = (LayoutParams) view.getLayoutParams(); - lp.setMarginStart(start); - lp.setMarginEnd(end); - view.setLayoutParams(lp); + if (lp != null) { + lp.setMarginStart(start); + lp.setMarginEnd(end); + view.setLayoutParams(lp); + } } public MediaHost getMediaHost() { @@ -1140,6 +944,14 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne mMediaVisibilityChangedListener = visibilityChangedListener; } + public boolean isListening() { + return mListening; + } + + public void setSecurityFooter(View view) { + mSecurityFooter = view; + } + private class H extends Handler { private static final int SHOW_DETAIL = 1; private static final int SET_TILE_VISIBILITY = 2; @@ -1155,46 +967,32 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne } } - @Override - public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - pw.println(getClass().getSimpleName() + ":"); - pw.println(" Tile records:"); - for (TileRecord record : mRecords) { - if (record.tile instanceof Dumpable) { - pw.print(" "); ((Dumpable) record.tile).dump(fd, pw, args); - pw.print(" "); pw.println(record.tileView.toString()); - } - } - } - - protected static class Record { DetailAdapter detailAdapter; int x; int y; } - public static final class TileRecord extends Record { - public QSTile tile; - public com.android.systemui.plugins.qs.QSTileView tileView; - public boolean scanState; - public QSTile.Callback callback; - } - public interface QSTileLayout { - + /** */ default void saveInstanceState(Bundle outState) {} + /** */ default void restoreInstanceState(Bundle savedInstanceState) {} - void addTile(TileRecord tile); + /** */ + void addTile(QSPanelControllerBase.TileRecord tile); - void removeTile(TileRecord tile); + /** */ + void removeTile(QSPanelControllerBase.TileRecord tile); - int getOffsetTop(TileRecord tile); + /** */ + int getOffsetTop(QSPanelControllerBase.TileRecord tile); + /** */ boolean updateResources(); + /** */ void setListening(boolean listening); /** @@ -1221,4 +1019,8 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne int getNumVisibleTiles(); } + + interface OnConfigurationChangedListener { + void onConfigurationChange(Configuration newConfig); + } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java new file mode 100644 index 000000000000..f222b0d1ccc0 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java @@ -0,0 +1,201 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs; + +import static com.android.systemui.qs.QSPanel.QS_SHOW_BRIGHTNESS; + +import android.annotation.NonNull; +import android.content.res.Configuration; +import android.view.View; +import android.view.ViewGroup; + +import com.android.internal.logging.MetricsLogger; +import com.android.internal.logging.UiEventLogger; +import com.android.systemui.R; +import com.android.systemui.dump.DumpManager; +import com.android.systemui.media.MediaHost; +import com.android.systemui.plugins.qs.QSTile; +import com.android.systemui.qs.customize.QSCustomizer; +import com.android.systemui.qs.dagger.QSScope; +import com.android.systemui.settings.BrightnessController; +import com.android.systemui.statusbar.policy.BrightnessMirrorController; +import com.android.systemui.tuner.TunerService; + +import javax.inject.Inject; + +/** + * Controller for {@link QSPanel}. + */ +@QSScope +public class QSPanelController extends QSPanelControllerBase<QSPanel> { + private final QSSecurityFooter mQsSecurityFooter; + private final TunerService mTunerService; + private final BrightnessController mBrightnessController; + + private final QSPanel.OnConfigurationChangedListener mOnConfigurationChangedListener = + new QSPanel.OnConfigurationChangedListener() { + @Override + public void onConfigurationChange(Configuration newConfig) { + mView.updateResources(); + mQsSecurityFooter.onConfigurationChanged(); + if (mView.isListening()) { + refreshAllTiles(); + } + } + }; + private BrightnessMirrorController mBrightnessMirrorController; + + @Inject + QSPanelController(QSPanel view, QSSecurityFooter qsSecurityFooter, TunerService tunerService, + QSTileHost qstileHost, DumpManager dumpManager, + MetricsLogger metricsLogger, UiEventLogger uiEventLogger, + BrightnessController.Factory brightnessControllerFactory) { + super(view, qstileHost, metricsLogger, uiEventLogger, dumpManager); + mQsSecurityFooter = qsSecurityFooter; + mTunerService = tunerService; + mQsSecurityFooter.setHostEnvironment(qstileHost); + mBrightnessController = brightnessControllerFactory.create( + mView.findViewById(R.id.brightness_slider)); + } + + @Override + protected void onViewAttached() { + super.onViewAttached(); + mTunerService.addTunable(mView, QS_SHOW_BRIGHTNESS); + mView.updateResources(); + if (mView.isListening()) { + refreshAllTiles(); + } + mView.addOnConfigurationChangedListener(mOnConfigurationChangedListener); + mView.setSecurityFooter(mQsSecurityFooter.getView()); + switchTileLayout(true); + if (mBrightnessMirrorController != null) { + mBrightnessMirrorController.addCallback(mView); + } + } + + @Override + protected void onViewDetached() { + mTunerService.removeTunable(mView); + mView.removeOnConfigurationChangedListener(mOnConfigurationChangedListener); + if (mBrightnessMirrorController != null) { + mBrightnessMirrorController.removeCallback(mView); + } + super.onViewDetached(); + } + + /** TODO(b/168904199): Remove this method once view is controllerized. */ + QSPanel getView() { + return mView; + } + + /** + * Set the header container of quick settings. + */ + public void setHeaderContainer(@NonNull ViewGroup headerContainer) { + mView.setHeaderContainer(headerContainer); + } + + public QSPanel.QSTileLayout getTileLayout() { + return mView.getTileLayout(); + } + + /** */ + public void setCustomizer(QSCustomizer customizer) { + mView.setCustomizer(customizer); + } + + /** */ + public boolean isShowingCustomize() { + return mView.isShowingCustomize(); + } + + /** */ + public void setVisibility(int visibility) { + mView.setVisibility(visibility); + } + + /** */ + public void setListening(boolean listening, boolean expanded) { + setListening(listening && expanded); + if (mView.isListening()) { + refreshAllTiles(); + } + + mQsSecurityFooter.setListening(listening); + + // Set the listening as soon as the QS fragment starts listening regardless of the + //expansion, so it will update the current brightness before the slider is visible. + if (listening) { + mBrightnessController.registerCallbacks(); + } else { + mBrightnessController.unregisterCallbacks(); + } + } + + /** */ + public QSTileRevealController getQsTileRevealController() { + return mView.getQsTileRevealController(); + } + + /** */ + public MediaHost getMediaHost() { + return mView.getMediaHost(); + } + + /** */ + public void setBrightnessMirror(BrightnessMirrorController brightnessMirrorController) { + mBrightnessMirrorController = brightnessMirrorController; + mView.setBrightnessMirror(brightnessMirrorController); + } + + /** Get the QSTileHost this panel uses. */ + public QSTileHost getHost() { + return mHost; + } + + + /** Open the details for a specific tile.. */ + public void openDetails(String subPanel) { + QSTile tile = getTile(subPanel); + if (tile != null) { + mView.openDetails(tile); + } + } + + /** Show the device monitoring dialog. */ + public void showDeviceMonitoringDialog() { + mQsSecurityFooter.showDeviceMonitoringDialog(); + } + + /** Update appearance of QSPanel. */ + public void updateResources() { + mView.updateResources(); + } + + /** Update state of all tiles. */ + public void refreshAllTiles() { + mBrightnessController.checkRestrictionAndSetEnabled(); + super.refreshAllTiles(); + mQsSecurityFooter.refreshState(); + } + + /** Start customizing the Quick Settings. */ + public void showEdit(View view) { + mView.showEdit(view); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java new file mode 100644 index 000000000000..fe92827806c6 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java @@ -0,0 +1,250 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs; + +import static com.android.internal.logging.nano.MetricsProto.MetricsEvent; + +import android.content.ComponentName; +import android.content.res.Configuration; +import android.metrics.LogMaker; + +import com.android.internal.logging.MetricsLogger; +import com.android.internal.logging.UiEventLogger; +import com.android.systemui.Dumpable; +import com.android.systemui.dump.DumpManager; +import com.android.systemui.media.MediaHost; +import com.android.systemui.plugins.qs.QSTile; +import com.android.systemui.plugins.qs.QSTileView; +import com.android.systemui.qs.external.CustomTile; +import com.android.systemui.util.ViewController; + +import java.io.FileDescriptor; +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.Collection; +import java.util.stream.Collectors; + +/** + * Controller for QSPanel views. + * + * @param <T> Type of QSPanel. + */ +public abstract class QSPanelControllerBase<T extends QSPanel> extends ViewController<T> + implements Dumpable{ + protected final QSTileHost mHost; + private final MediaHost mMediaHost; + private final MetricsLogger mMetricsLogger; + private final UiEventLogger mUiEventLogger; + private final DumpManager mDumpManager; + protected final ArrayList<TileRecord> mRecords = new ArrayList<>(); + + private int mLastOrientation; + + private final QSHost.Callback mQSHostCallback = this::setTiles; + + private final QSPanel.OnConfigurationChangedListener mOnConfigurationChangedListener = + new QSPanel.OnConfigurationChangedListener() { + @Override + public void onConfigurationChange(Configuration newConfig) { + if (newConfig.orientation != mLastOrientation) { + mLastOrientation = newConfig.orientation; + switchTileLayout(false); + } + } + }; + private String mCachedSpecs = ""; + + protected QSPanelControllerBase(T view, QSTileHost host, + MetricsLogger metricsLogger, UiEventLogger uiEventLogger, DumpManager dumpManager) { + super(view); + mHost = host; + mMediaHost = mView.getMediaHost(); + mMetricsLogger = metricsLogger; + mUiEventLogger = uiEventLogger; + mDumpManager = dumpManager; + } + + @Override + protected void onViewAttached() { + mView.addOnConfigurationChangedListener(mOnConfigurationChangedListener); + mHost.addCallback(mQSHostCallback); + mMediaHost.addVisibilityChangeListener(aBoolean -> { + switchTileLayout(false); + return null; + }); + setTiles(); + switchTileLayout(true); + mDumpManager.registerDumpable(mView.getDumpableTag(), this); + } + + @Override + protected void onViewDetached() { + mView.removeOnConfigurationChangedListener(mOnConfigurationChangedListener); + mHost.removeCallback(mQSHostCallback); + + for (TileRecord record : mRecords) { + record.tile.removeCallbacks(); + } + mRecords.clear(); + mDumpManager.unregisterDumpable(mView.getDumpableTag()); + } + + /** */ + public void setTiles() { + setTiles(mHost.getTiles(), false); + } + + /** */ + public void setTiles(Collection<QSTile> tiles, boolean collapsedView) { + if (!collapsedView) { + mView.updateRevealedTiles(tiles); + } + for (QSPanelControllerBase.TileRecord record : mRecords) { + mView.removeTile(record); + record.tile.removeCallback(record.callback); + } + mRecords.clear(); + mCachedSpecs = ""; + for (QSTile tile : tiles) { + addTile(tile, collapsedView); + } + } + + /** */ + public void refreshAllTiles() { + for (QSPanelControllerBase.TileRecord r : mRecords) { + r.tile.refreshState(); + } + } + + private void addTile(final QSTile tile, boolean collapsedView) { + final TileRecord r = new TileRecord(); + r.tile = tile; + r.tileView = mHost.createTileView(tile, collapsedView); + mView.addTile(r); + mRecords.add(r); + mCachedSpecs = getTilesSpecs(); + + } + + /** */ + public void clickTile(ComponentName tile) { + final String spec = CustomTile.toSpec(tile); + for (TileRecord record : mRecords) { + if (record.tile.getTileSpec().equals(spec)) { + record.tile.click(); + break; + } + } + } + protected QSTile getTile(String subPanel) { + for (int i = 0; i < mRecords.size(); i++) { + if (subPanel.equals(mRecords.get(i).tile.getTileSpec())) { + return mRecords.get(i).tile; + } + } + return mHost.createTile(subPanel); + } + + + QSTileView getTileView(QSTile tile) { + for (QSPanelControllerBase.TileRecord r : mRecords) { + if (r.tile == tile) { + return r.tileView; + } + } + return null; + } + + private String getTilesSpecs() { + return mRecords.stream() + .map(tileRecord -> tileRecord.tile.getTileSpec()) + .collect(Collectors.joining(",")); + } + + + /** */ + public void setExpanded(boolean expanded) { + mView.setExpanded(expanded); + mMetricsLogger.visibility(MetricsEvent.QS_PANEL, expanded); + if (!expanded) { + mUiEventLogger.log(mView.closePanelEvent()); + closeDetail(); + } else { + mUiEventLogger.log(mView.openPanelEvent()); + logTiles(); + } + } + + /** */ + public void closeDetail() { + mView.closeDetail(); + } + + /** */ + public void openDetails(String subPanel) { + QSTile tile = getTile(subPanel); + // If there's no tile with that name (as defined in QSFactoryImpl or other QSFactory), + // QSFactory will not be able to create a tile and getTile will return null + if (tile != null) { + mView.showDetailAdapter( + true, tile.getDetailAdapter(), new int[]{mView.getWidth() / 2, 0}); + } + } + + + void setListening(boolean listening) { + mView.setListening(listening, mCachedSpecs); + } + + boolean switchTileLayout(boolean force) { + if (mView.switchTileLayout(force, mRecords)) { + setTiles(); + return true; + } + return false; + } + + private void logTiles() { + for (int i = 0; i < mRecords.size(); i++) { + QSTile tile = mRecords.get(i).tile; + mMetricsLogger.write(tile.populate(new LogMaker(tile.getMetricsCategory()) + .setType(MetricsEvent.TYPE_OPEN))); + } + } + + + @Override + public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + pw.println(getClass().getSimpleName() + ":"); + pw.println(" Tile records:"); + for (QSPanelControllerBase.TileRecord record : mRecords) { + if (record.tile instanceof Dumpable) { + pw.print(" "); ((Dumpable) record.tile).dump(fd, pw, args); + pw.print(" "); pw.println(record.tileView.toString()); + } + } + } + + /** */ + public static final class TileRecord extends QSPanel.Record { + public QSTile tile; + public com.android.systemui.plugins.qs.QSTileView tileView; + public boolean scanState; + public QSTile.Callback callback; + } +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java b/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java index 0891972c11d2..c90182b15da6 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java @@ -44,11 +44,15 @@ import com.android.systemui.Dependency; import com.android.systemui.FontSizeUtils; import com.android.systemui.R; import com.android.systemui.plugins.ActivityStarter; +import com.android.systemui.qs.dagger.QSScope; import com.android.systemui.settings.UserTracker; import com.android.systemui.statusbar.phone.SystemUIDialog; import com.android.systemui.statusbar.policy.SecurityController; -public class QSSecurityFooter implements OnClickListener, DialogInterface.OnClickListener { +import javax.inject.Inject; + +@QSScope +class QSSecurityFooter implements OnClickListener, DialogInterface.OnClickListener { protected static final String TAG = "QSSecurityFooter"; protected static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); private static final boolean DEBUG_FORCE_VISIBLE = false; @@ -72,12 +76,13 @@ public class QSSecurityFooter implements OnClickListener, DialogInterface.OnClic private int mFooterTextId; private int mFooterIconId; + @Inject public QSSecurityFooter(QSPanel qsPanel, Context context, UserTracker userTracker) { mRootView = LayoutInflater.from(context) .inflate(R.layout.quick_settings_footer, qsPanel, false); mRootView.setOnClickListener(this); - mFooterText = (TextView) mRootView.findViewById(R.id.footer_text); - mFooterIcon = (ImageView) mRootView.findViewById(R.id.footer_icon); + mFooterText = mRootView.findViewById(R.id.footer_text); + mFooterIcon = mRootView.findViewById(R.id.footer_icon); mFooterIconId = R.drawable.ic_info_outline; mContext = context; mMainHandler = new Handler(Looper.myLooper()); diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileRevealController.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileRevealController.java index 2f012e6e608e..3d4a417abf2e 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSTileRevealController.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileRevealController.java @@ -8,11 +8,16 @@ import android.util.ArraySet; import com.android.systemui.Prefs; import com.android.systemui.plugins.qs.QSTile; +import com.android.systemui.qs.dagger.QSScope; import java.util.Collection; import java.util.Collections; import java.util.Set; +import javax.inject.Inject; + +/** */ +@QSScope public class QSTileRevealController { private static final long QS_REVEAL_TILES_DELAY = 500L; @@ -34,6 +39,7 @@ public class QSTileRevealController { } }; + @Inject QSTileRevealController(Context context, QSPanel qsPanel, PagedTileLayout pagedTileLayout) { mContext = context; mQSPanel = qsPanel; diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java index ea036f6fe0e5..84a5b6f0538d 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java @@ -16,6 +16,7 @@ package com.android.systemui.qs; +import static com.android.systemui.media.dagger.MediaModule.QUICK_QS_PANEL; import static com.android.systemui.util.InjectionInflationController.VIEW_CONTEXT; import android.content.Context; @@ -27,23 +28,13 @@ import android.view.View; import android.widget.LinearLayout; import com.android.internal.logging.UiEventLogger; -import com.android.systemui.Dependency; import com.android.systemui.R; -import com.android.systemui.broadcast.BroadcastDispatcher; -import com.android.systemui.dump.DumpManager; import com.android.systemui.media.MediaHierarchyManager; import com.android.systemui.media.MediaHost; import com.android.systemui.plugins.qs.QSTile; import com.android.systemui.plugins.qs.QSTile.SignalState; import com.android.systemui.plugins.qs.QSTile.State; -import com.android.systemui.qs.customize.QSCustomizer; import com.android.systemui.qs.logging.QSLogger; -import com.android.systemui.settings.UserTracker; -import com.android.systemui.tuner.TunerService; -import com.android.systemui.tuner.TunerService.Tunable; - -import java.util.ArrayList; -import java.util.Collection; import javax.inject.Inject; import javax.inject.Named; @@ -67,15 +58,10 @@ public class QuickQSPanel extends QSPanel { public QuickQSPanel( @Named(VIEW_CONTEXT) Context context, AttributeSet attrs, - DumpManager dumpManager, - BroadcastDispatcher broadcastDispatcher, QSLogger qsLogger, - MediaHost mediaHost, - UiEventLogger uiEventLogger, - UserTracker userTracker - ) { - super(context, attrs, dumpManager, broadcastDispatcher, qsLogger, mediaHost, uiEventLogger, - userTracker); + @Named(QUICK_QS_PANEL) MediaHost mediaHost, + UiEventLogger uiEventLogger) { + super(context, attrs, qsLogger, mediaHost, uiEventLogger); sDefaultMaxTiles = getResources().getInteger(R.integer.quick_qs_panel_max_columns); applyBottomMargin((View) mRegularTileLayout); } @@ -88,17 +74,12 @@ public class QuickQSPanel extends QSPanel { } @Override - protected void addSecurityFooter() { - // No footer needed - } - - @Override protected void addViewsAboveTiles() { // Nothing to add above the tiles } @Override - protected TileLayout createRegularTileLayout() { + public TileLayout createRegularTileLayout() { return new QuickQSPanel.HeaderTileLayout(mContext, mUiEventLogger); } @@ -108,6 +89,7 @@ public class QuickQSPanel extends QSPanel { } @Override + protected void initMediaHostState() { mMediaHost.setExpansion(0.0f); mMediaHost.setShowsOnlyActiveMedia(true); @@ -131,18 +113,6 @@ public class QuickQSPanel extends QSPanel { } @Override - protected void onAttachedToWindow() { - super.onAttachedToWindow(); - Dependency.get(TunerService.class).addTunable(mNumTiles, NUM_QUICK_TILES); - } - - @Override - protected void onDetachedFromWindow() { - super.onDetachedFromWindow(); - Dependency.get(TunerService.class).removeTunable(mNumTiles); - } - - @Override protected String getDumpableTag() { return TAG; } @@ -157,7 +127,7 @@ public class QuickQSPanel extends QSPanel { } @Override - protected void drawTile(TileRecord r, State state) { + protected void drawTile(QSPanelControllerBase.TileRecord r, State state) { if (state instanceof SignalState) { SignalState copy = new SignalState(); state.copyTo(copy); @@ -169,17 +139,8 @@ public class QuickQSPanel extends QSPanel { super.drawTile(r, state); } - @Override - public void setHost(QSTileHost host, QSCustomizer customizer) { - super.setHost(host, customizer); - setTiles(mHost.getTiles()); - } - public void setMaxTiles(int maxTiles) { mMaxTiles = maxTiles; - if (mHost != null) { - setTiles(mHost.getTiles()); - } } @Override @@ -190,25 +151,6 @@ public class QuickQSPanel extends QSPanel { } } - @Override - public void setTiles(Collection<QSTile> tiles) { - ArrayList<QSTile> quickTiles = new ArrayList<>(); - for (QSTile tile : tiles) { - quickTiles.add(tile); - if (quickTiles.size() == mMaxTiles) { - break; - } - } - super.setTiles(quickTiles, true); - } - - private final Tunable mNumTiles = new Tunable() { - @Override - public void onTuningChanged(String key, String newValue) { - setMaxTiles(parseNumTiles(newValue)); - } - }; - public int getNumQuickTiles() { return mMaxTiles; } @@ -306,7 +248,7 @@ public class QuickQSPanel extends QSPanel { } @Override - protected void addTileView(TileRecord tile) { + protected void addTileView(QSPanelControllerBase.TileRecord tile) { addView(tile.tileView, getChildCount(), generateTileLayoutParams()); } @@ -369,7 +311,7 @@ public class QuickQSPanel extends QSPanel { private void setAccessibilityOrder() { if (mRecords != null && mRecords.size() > 0) { View previousView = this; - for (TileRecord record : mRecords) { + for (QSPanelControllerBase.TileRecord record : mRecords) { if (record.tileView.getVisibility() == GONE) continue; previousView = record.tileView.updateAccessibilityOrder(previousView); } @@ -381,7 +323,7 @@ public class QuickQSPanel extends QSPanel { @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { // Measure each QS tile. - for (TileRecord record : mRecords) { + for (QSPanelControllerBase.TileRecord record : mRecords) { if (record.tileView.getVisibility() == GONE) continue; record.tileView.measure(exactly(mCellWidth), exactly(mCellHeight)); } diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java new file mode 100644 index 000000000000..97b6e99a0390 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs; + +import static com.android.systemui.qs.QuickQSPanel.NUM_QUICK_TILES; +import static com.android.systemui.qs.QuickQSPanel.parseNumTiles; + +import com.android.internal.logging.MetricsLogger; +import com.android.internal.logging.UiEventLogger; +import com.android.systemui.dump.DumpManager; +import com.android.systemui.plugins.qs.QSTile; +import com.android.systemui.qs.dagger.QSScope; +import com.android.systemui.tuner.TunerService; +import com.android.systemui.tuner.TunerService.Tunable; + +import java.util.ArrayList; + +import javax.inject.Inject; + +/** Controller for {@link QuickQSPanel}. */ +@QSScope +public class QuickQSPanelController extends QSPanelControllerBase<QuickQSPanel> { + private final Tunable mNumTiles = + (key, newValue) -> setMaxTiles(parseNumTiles(newValue)); + + private final TunerService mTunerService; + + @Inject + QuickQSPanelController(QuickQSPanel view, TunerService tunerService, QSTileHost qsTileHost, + MetricsLogger metricsLogger, UiEventLogger uiEventLogger, + DumpManager dumpManager) { + super(view, qsTileHost, metricsLogger, uiEventLogger, dumpManager); + mTunerService = tunerService; + } + + @Override + protected void onViewAttached() { + super.onViewAttached(); + mTunerService.addTunable(mNumTiles, NUM_QUICK_TILES); + + } + + @Override + protected void onViewDetached() { + super.onViewDetached(); + mTunerService.removeTunable(mNumTiles); + } + + public boolean isListening() { + return mView.isListening(); + } + + private void setMaxTiles(int parseNumTiles) { + mView.setMaxTiles(parseNumTiles); + setTiles(); + } + + @Override + public void setTiles() { + ArrayList<QSTile> quickTiles = new ArrayList<>(); + for (QSTile tile : mHost.getTiles()) { + quickTiles.add(tile); + if (quickTiles.size() == mView.getNumQuickTiles()) { + break; + } + } + super.setTiles(quickTiles, true); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java index a9fbc744b38e..5757602b9d0f 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java @@ -353,11 +353,7 @@ public class QuickStatusBarHeader extends RelativeLayout implements LifecycleOwn mPrivacyChip.setExpanded(expansionFraction > 0.5); mPrivacyChipAlphaAnimator.setPosition(keyguardExpansionFraction); } - if (expansionFraction < 1 && expansionFraction > 0.99) { - if (mHeaderQsPanel.switchTileLayout()) { - updateResources(); - } - } + mKeyguardExpansionFraction = keyguardExpansionFraction; } @@ -446,7 +442,6 @@ public class QuickStatusBarHeader extends RelativeLayout implements LifecycleOwn public void setQSPanel(final QSPanel qsPanel) { //host.setHeaderView(mExpandIndicator); mHeaderQsPanel.setQSPanelAndHeader(qsPanel, this); - mHeaderQsPanel.setHost(qsPanel.getHost(), null /* No customization in header */); Rect tintArea = new Rect(0, 0, 0, 0); int colorForeground = Utils.getColorAttrDefaultColor(getContext(), diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java index 676a300b0ff2..febb71c3223b 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java @@ -44,6 +44,7 @@ import com.android.systemui.privacy.PrivacyChipEvent; import com.android.systemui.privacy.PrivacyItem; import com.android.systemui.privacy.PrivacyItemController; import com.android.systemui.qs.carrier.QSCarrierGroupController; +import com.android.systemui.qs.dagger.QSScope; import com.android.systemui.settings.UserTracker; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.phone.StatusBarIconController; @@ -64,6 +65,7 @@ import javax.inject.Inject; /** * Controller for {@link QuickStatusBarHeader}. */ +@QSScope class QuickStatusBarHeaderController extends ViewController<QuickStatusBarHeader> { private static final String TAG = "QuickStatusBarHeader"; @@ -74,7 +76,7 @@ class QuickStatusBarHeaderController extends ViewController<QuickStatusBarHeader private final ActivityStarter mActivityStarter; private final UiEventLogger mUiEventLogger; private final QSCarrierGroupController mQSCarrierGroupController; - private final QuickQSPanel mHeaderQsPanel; + private final QuickQSPanelController mHeaderQsPanelController; private final LifecycleRegistry mLifecycle; private final OngoingPrivacyChip mPrivacyChip; private final Clock mClockView; @@ -93,6 +95,7 @@ class QuickStatusBarHeaderController extends ViewController<QuickStatusBarHeader private AlarmClockInfo mNextAlarm; private boolean mAllIndicatorsEnabled; private boolean mMicCameraIndicatorsEnabled; + private boolean mLocationIndicatorsEnabled; private boolean mPrivacyChipLogged; private int mRingerMode = AudioManager.RINGER_MODE_NORMAL; @@ -156,6 +159,14 @@ class QuickStatusBarHeaderController extends ViewController<QuickStatusBarHeader } } + @Override + public void onFlagLocationChanged(boolean flag) { + if (mLocationIndicatorsEnabled != flag) { + mLocationIndicatorsEnabled = flag; + update(); + } + } + private void update() { StatusIconContainer iconContainer = mView.requireViewById(R.id.statusIcons); iconContainer.setIgnoredSlots(getIgnoredIconSlots()); @@ -194,13 +205,14 @@ class QuickStatusBarHeaderController extends ViewController<QuickStatusBarHeader } }; - private QuickStatusBarHeaderController(QuickStatusBarHeader view, + @Inject + QuickStatusBarHeaderController(QuickStatusBarHeader view, ZenModeController zenModeController, NextAlarmController nextAlarmController, PrivacyItemController privacyItemController, RingerModeTracker ringerModeTracker, ActivityStarter activityStarter, UiEventLogger uiEventLogger, QSTileHost qsTileHost, StatusBarIconController statusBarIconController, CommandQueue commandQueue, DemoModeController demoModeController, - UserTracker userTracker, + UserTracker userTracker, QuickQSPanelController quickQSPanelController, QSCarrierGroupController.Builder qsCarrierGroupControllerBuilder) { super(view); mZenModeController = zenModeController; @@ -215,6 +227,7 @@ class QuickStatusBarHeaderController extends ViewController<QuickStatusBarHeader mDemoModeController = demoModeController; mUserTracker = userTracker; mLifecycle = new LifecycleRegistry(mLifecycleOwner); + mHeaderQsPanelController = quickQSPanelController; mQSCarrierGroupController = qsCarrierGroupControllerBuilder .setQSCarrierGroup(mView.findViewById(R.id.carrier_group)) @@ -222,7 +235,6 @@ class QuickStatusBarHeaderController extends ViewController<QuickStatusBarHeader mPrivacyChip = mView.findViewById(R.id.privacy_chip); - mHeaderQsPanel = mView.findViewById(R.id.quick_qs_panel); mNextAlarmContainer = mView.findViewById(R.id.alarm_container); mClockView = mView.findViewById(R.id.clock); mRingerContainer = mView.findViewById(R.id.ringer_container); @@ -252,6 +264,7 @@ class QuickStatusBarHeaderController extends ViewController<QuickStatusBarHeader mAllIndicatorsEnabled = mPrivacyItemController.getAllIndicatorsAvailable(); mMicCameraIndicatorsEnabled = mPrivacyItemController.getMicCameraAvailable(); + mLocationIndicatorsEnabled = mPrivacyItemController.getLocationAvailable(); setChipVisibility(mPrivacyChip.getVisibility() == View.VISIBLE); @@ -280,8 +293,12 @@ class QuickStatusBarHeaderController extends ViewController<QuickStatusBarHeader } mListening = listening; - mHeaderQsPanel.setListening(listening); - if (mHeaderQsPanel.switchTileLayout()) { + mHeaderQsPanelController.setListening(listening); + if (mHeaderQsPanelController.isListening()) { + mHeaderQsPanelController.refreshAllTiles(); + } + + if (mHeaderQsPanelController.switchTileLayout(false)) { mView.updateResources(); } @@ -292,6 +309,7 @@ class QuickStatusBarHeaderController extends ViewController<QuickStatusBarHeader // Get the most up to date info mAllIndicatorsEnabled = mPrivacyItemController.getAllIndicatorsAvailable(); mMicCameraIndicatorsEnabled = mPrivacyItemController.getMicCameraAvailable(); + mLocationIndicatorsEnabled = mPrivacyItemController.getLocationAvailable(); mPrivacyItemController.addCallback(mPICCallback); } else { mZenModeController.removeCallback(mZenModeControllerCallback); @@ -323,7 +341,7 @@ class QuickStatusBarHeaderController extends ViewController<QuickStatusBarHeader com.android.internal.R.string.status_bar_camera)); ignored.add(mView.getResources().getString( com.android.internal.R.string.status_bar_microphone)); - if (mAllIndicatorsEnabled) { + if (mAllIndicatorsEnabled || mLocationIndicatorsEnabled) { ignored.add(mView.getResources().getString( com.android.internal.R.string.status_bar_location)); } @@ -333,7 +351,7 @@ class QuickStatusBarHeaderController extends ViewController<QuickStatusBarHeader } private boolean getChipEnabled() { - return mMicCameraIndicatorsEnabled || mAllIndicatorsEnabled; + return mMicCameraIndicatorsEnabled || mLocationIndicatorsEnabled || mAllIndicatorsEnabled; } private boolean isZenOverridingRinger() { @@ -369,55 +387,4 @@ class QuickStatusBarHeaderController extends ViewController<QuickStatusBarHeader mClockView.onDemoModeFinished(); } } - - static class Builder { - private final ZenModeController mZenModeController; - private final NextAlarmController mNextAlarmController; - private final PrivacyItemController mPrivacyItemController; - private final RingerModeTracker mRingerModeTracker; - private final ActivityStarter mActivityStarter; - private final UiEventLogger mUiEventLogger; - private final QSTileHost mQsTileHost; - private final StatusBarIconController mStatusBarIconController; - private final CommandQueue mCommandQueue; - private final DemoModeController mDemoModeController; - private final UserTracker mUserTracker; - private final QSCarrierGroupController.Builder mQSCarrierGroupControllerBuilder; - private QuickStatusBarHeader mView; - - @Inject - Builder(ZenModeController zenModeController, NextAlarmController nextAlarmController, - PrivacyItemController privacyItemController, RingerModeTracker ringerModeTracker, - ActivityStarter activityStarter, UiEventLogger uiEventLogger, QSTileHost qsTileHost, - StatusBarIconController statusBarIconController, CommandQueue commandQueue, - DemoModeController demoModeController, UserTracker userTracker, - QSCarrierGroupController.Builder qsCarrierGroupControllerBuilder) { - mZenModeController = zenModeController; - mNextAlarmController = nextAlarmController; - mPrivacyItemController = privacyItemController; - mRingerModeTracker = ringerModeTracker; - mActivityStarter = activityStarter; - mUiEventLogger = uiEventLogger; - mQsTileHost = qsTileHost; - mStatusBarIconController = statusBarIconController; - mCommandQueue = commandQueue; - mDemoModeController = demoModeController; - mUserTracker = userTracker; - mQSCarrierGroupControllerBuilder = qsCarrierGroupControllerBuilder; - } - - public Builder setQuickStatusBarHeader(QuickStatusBarHeader view) { - mView = view; - return this; - } - - - QuickStatusBarHeaderController build() { - return new QuickStatusBarHeaderController(mView, mZenModeController, - mNextAlarmController, mPrivacyItemController, mRingerModeTracker, - mActivityStarter, mUiEventLogger, mQsTileHost, mStatusBarIconController, - mCommandQueue, mDemoModeController, mUserTracker, - mQSCarrierGroupControllerBuilder); - } - } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java index 694492a33524..4ab7afd46602 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java +++ b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java @@ -11,7 +11,7 @@ import android.view.ViewGroup; import com.android.systemui.R; import com.android.systemui.qs.QSPanel.QSTileLayout; -import com.android.systemui.qs.QSPanel.TileRecord; +import com.android.systemui.qs.QSPanelControllerBase.TileRecord; import java.util.ArrayList; diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java index 55b67e061c13..8097958fac39 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java +++ b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java @@ -74,9 +74,9 @@ public class QSCustomizer extends LinearLayout implements OnMenuItemClickListene private final ScreenLifecycle mScreenLifecycle; private final TileQueryHelper mTileQueryHelper; private final View mTransparentView; + private final QSTileHost mHost; private boolean isShown; - private QSTileHost mHost; private RecyclerView mRecyclerView; private TileAdapter mTileAdapter; private Toolbar mToolbar; @@ -95,6 +95,7 @@ public class QSCustomizer extends LinearLayout implements OnMenuItemClickListene KeyguardStateController keyguardStateController, ScreenLifecycle screenLifecycle, TileQueryHelper tileQueryHelper, + QSTileHost qsTileHost, UiEventLogger uiEventLogger) { super(new ContextThemeWrapper(context, R.style.edit_theme), attrs); @@ -139,6 +140,8 @@ public class QSCustomizer extends LinearLayout implements OnMenuItemClickListene mLightBarController = lightBarController; mKeyguardStateController = keyguardStateController; mScreenLifecycle = screenLifecycle; + mHost = qsTileHost; + mTileAdapter.setHost(mHost); updateNavBackDrop(getResources().getConfiguration()); } @@ -170,11 +173,6 @@ public class QSCustomizer extends LinearLayout implements OnMenuItemClickListene mLightBarController.setQsCustomizing(mIsShowingNavBackdrop && isShown); } - public void setHost(QSTileHost host) { - mHost = host; - mTileAdapter.setHost(host); - } - public void setContainer(NotificationsQuickSettingsContainer notificationsQsContainer) { mNotifQsContainer = notificationsQsContainer; } diff --git a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentComponent.java b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentComponent.java new file mode 100644 index 000000000000..51b2c8dfffbd --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentComponent.java @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs.dagger; + +import com.android.systemui.qs.QSAnimator; +import com.android.systemui.qs.QSContainerImplController; +import com.android.systemui.qs.QSFooter; +import com.android.systemui.qs.QSFragment; +import com.android.systemui.qs.QSPanelController; +import com.android.systemui.qs.QuickQSPanelController; + +import dagger.BindsInstance; +import dagger.Subcomponent; + +/** + * Dagger Subcomponent for {@link QSFragment}. + */ +@Subcomponent(modules = {QSFragmentModule.class}) +@QSScope +public interface QSFragmentComponent { + /** Factory for building a {@link QSFragmentComponent}. */ + @Subcomponent.Factory + interface Factory { + QSFragmentComponent create(@BindsInstance QSFragment qsFragment); + } + + /** Construct a {@link QSPanelController}. */ + QSPanelController getQSPanelController(); + + /** Construct a {@link QuickQSPanelController}. */ + QuickQSPanelController getQuickQSPanelController(); + + /** Construct a {@link QSAnimator}. */ + QSAnimator getQSAnimator(); + + /** Construct a {@link QSContainerImplController}. */ + QSContainerImplController getQSContainerImplController(); + + /** Construct a {@link QSFooter} */ + QSFooter getQSFooter(); +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentModule.java b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentModule.java new file mode 100644 index 000000000000..4bf4eff4c27e --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentModule.java @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs.dagger; + +import android.view.View; + +import com.android.systemui.R; +import com.android.systemui.dagger.qualifiers.RootView; +import com.android.systemui.plugins.qs.QS; +import com.android.systemui.qs.QSContainerImpl; +import com.android.systemui.qs.QSFooter; +import com.android.systemui.qs.QSFooterView; +import com.android.systemui.qs.QSFooterViewController; +import com.android.systemui.qs.QSFragment; +import com.android.systemui.qs.QSPanel; +import com.android.systemui.qs.QuickQSPanel; +import com.android.systemui.qs.QuickStatusBarHeader; + +import dagger.Binds; +import dagger.Module; +import dagger.Provides; + +/** + * Dagger Module for {@link QSFragmentComponent}. + */ +@Module +public interface QSFragmentModule { + /** */ + @Provides + @RootView + static View provideRootView(QSFragment qsFragment) { + return qsFragment.getView(); + } + + /** */ + @Provides + static QSPanel provideQSPanel(@RootView View view) { + return view.findViewById(R.id.quick_settings_panel); + } + + /** */ + @Provides + static QSContainerImpl providesQSContainerImpl(@RootView View view) { + return view.findViewById(R.id.quick_settings_container); + } + + /** */ + @Binds + QS bindQS(QSFragment qsFragment); + + /** */ + @Provides + static QuickStatusBarHeader providesQuickStatusBarHeader(@RootView View view) { + return view.findViewById(R.id.header); + } + + /** */ + @Provides + static QuickQSPanel providesQuickQSPanel(QuickStatusBarHeader quickStatusBarHeader) { + return quickStatusBarHeader.findViewById(R.id.quick_qs_panel); + } + + /** */ + @Provides + static QSFooterView providesQSFooterView(@RootView View view) { + return view.findViewById(R.id.qs_footer); + } + + /** */ + @Provides + @QSScope + static QSFooter providesQSFooter(QSFooterViewController qsFooterViewController) { + qsFooterViewController.init(); + return qsFooterViewController; + } +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java index 8ff96c8a4a37..7c799aefe90d 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java +++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java @@ -21,6 +21,7 @@ import android.hardware.display.NightDisplayListener; import android.os.Handler; import com.android.systemui.dagger.qualifiers.Background; +import com.android.systemui.media.dagger.MediaModule; import com.android.systemui.qs.AutoAddTracker; import com.android.systemui.qs.QSHost; import com.android.systemui.qs.QSTileHost; @@ -37,8 +38,8 @@ import dagger.Provides; /** * Module for QS dependencies */ -// TODO: Add other QS classes -@Module +@Module(subcomponents = {QSFragmentComponent.class}, + includes = {MediaModule.class}) public interface QSModule { @Provides @@ -59,7 +60,6 @@ public interface QSModule { return manager; } - /** */ @Binds QSHost provideQsHost(QSTileHost controllerImpl); diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ContextCompat.java b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSScope.java index 51fcb0a841ca..f615eabb67dc 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ContextCompat.java +++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSScope.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2018 The Android Open Source Project + * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,21 +14,19 @@ * limitations under the License. */ -package com.android.systemui.shared.system; +package com.android.systemui.qs.dagger; -import android.content.Context; +import static java.lang.annotation.RetentionPolicy.RUNTIME; -/** - * Wraps a context to expose some methods for launcher to call. - */ -public class ContextCompat { - private final Context mWrapped; +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; - public ContextCompat(Context context) { - mWrapped = context; - } +import javax.inject.Scope; - public int getUserId() { - return mWrapped.getUserId(); - } -} +/** + * Scope annotation for singleton items within the {@link QSFragmentComponent}. + */ +@Documented +@Retention(RUNTIME) +@Scope +public @interface QSScope {} diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java index 5279a20a67a7..ddf30ad663dd 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java +++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java @@ -95,7 +95,6 @@ import com.android.wm.shell.onehanded.OneHanded; import com.android.wm.shell.onehanded.OneHandedEvents; import com.android.wm.shell.pip.Pip; import com.android.wm.shell.pip.PipAnimationController; -import com.android.wm.shell.pip.phone.PipUtils; import com.android.wm.shell.splitscreen.SplitScreen; import java.io.FileDescriptor; @@ -151,7 +150,6 @@ public class OverviewProxyService extends CurrentUserTracker implements private int mConnectionBackoffAttempts; private boolean mBound; private boolean mIsEnabled; - private boolean mHasPipFeature; private int mCurrentBoundedUserId = -1; private float mNavBarButtonAlpha; private boolean mInputFocusTransferStarted; @@ -377,9 +375,7 @@ public class OverviewProxyService extends CurrentUserTracker implements @Override public void setShelfHeight(boolean visible, int shelfHeight) { - if (!verifyCaller("setShelfHeight") || !mHasPipFeature) { - Log.w(TAG_OPS, - "ByPass setShelfHeight, FEATURE_PICTURE_IN_PICTURE:" + mHasPipFeature); + if (!verifyCaller("setShelfHeight")) { return; } final long token = Binder.clearCallingIdentity(); @@ -405,9 +401,7 @@ public class OverviewProxyService extends CurrentUserTracker implements @Override public void notifySwipeToHomeFinished() { - if (!verifyCaller("notifySwipeToHomeFinished") || !mHasPipFeature) { - Log.w(TAG_OPS, "ByPass notifySwipeToHomeFinished, FEATURE_PICTURE_IN_PICTURE:" - + mHasPipFeature); + if (!verifyCaller("notifySwipeToHomeFinished")) { return; } final long token = Binder.clearCallingIdentity(); @@ -422,9 +416,7 @@ public class OverviewProxyService extends CurrentUserTracker implements @Override public void setPinnedStackAnimationListener(IPinnedStackAnimationListener listener) { - if (!verifyCaller("setPinnedStackAnimationListener") || !mHasPipFeature) { - Log.w(TAG_OPS, "ByPass setPinnedStackAnimationListener, FEATURE_PICTURE_IN_PICTURE:" - + mHasPipFeature); + if (!verifyCaller("setPinnedStackAnimationListener")) { return; } mIPinnedStackAnimationListener = listener; @@ -509,7 +501,7 @@ public class OverviewProxyService extends CurrentUserTracker implements public Rect startSwipePipToHome(ComponentName componentName, ActivityInfo activityInfo, PictureInPictureParams pictureInPictureParams, int launcherRotation, int shelfHeight) { - if (!verifyCaller("startSwipePipToHome") || !mHasPipFeature) { + if (!verifyCaller("startSwipePipToHome")) { return null; } final long binderToken = Binder.clearCallingIdentity(); @@ -525,7 +517,7 @@ public class OverviewProxyService extends CurrentUserTracker implements @Override public void stopSwipePipToHome(ComponentName componentName, Rect destinationBounds) { - if (!verifyCaller("stopSwipePipToHome") || !mHasPipFeature) { + if (!verifyCaller("stopSwipePipToHome")) { return; } final long binderToken = Binder.clearCallingIdentity(); @@ -650,7 +642,6 @@ public class OverviewProxyService extends CurrentUserTracker implements super(broadcastDispatcher); mContext = context; mPipOptional = pipOptional; - mHasPipFeature = PipUtils.hasSystemFeature(mContext); mStatusBarOptionalLazy = statusBarOptionalLazy; mHandler = new Handler(); mNavBarControllerLazy = navBarControllerLazy; diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java index 1a9abb9cf27d..e6f43c1ff1d2 100644 --- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java +++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenMediaRecorder.java @@ -132,7 +132,7 @@ public class ScreenMediaRecorder { mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264); mMediaRecorder.setVideoEncodingProfileLevel( MediaCodecInfo.CodecProfileLevel.AVCProfileHigh, - MediaCodecInfo.CodecProfileLevel.AVCLevel42); + MediaCodecInfo.CodecProfileLevel.AVCLevel3); mMediaRecorder.setVideoSize(screenWidth, screenHeight); mMediaRecorder.setVideoFrameRate(refereshRate); mMediaRecorder.setVideoEncodingBitRate(vidBitRate); diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java index aaa335c25d5d..818bb9d8d78f 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java @@ -18,8 +18,7 @@ package com.android.systemui.screenshot; import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; import static android.content.res.Configuration.ORIENTATION_PORTRAIT; -import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; -import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; +import static android.view.Display.DEFAULT_DISPLAY; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; @@ -30,6 +29,7 @@ import android.annotation.SuppressLint; import android.app.ActivityManager; import android.app.Notification; import android.app.PendingIntent; +import android.app.WindowContext; import android.content.ComponentName; import android.content.Context; import android.content.res.Configuration; @@ -45,8 +45,10 @@ import android.graphics.Region; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; +import android.graphics.drawable.Icon; import android.graphics.drawable.InsetDrawable; import android.graphics.drawable.LayerDrawable; +import android.hardware.display.DisplayManager; import android.media.MediaActionSound; import android.net.Uri; import android.os.Handler; @@ -83,7 +85,6 @@ import android.widget.Toast; import com.android.internal.logging.UiEventLogger; import com.android.systemui.R; import com.android.systemui.dagger.SysUISingleton; -import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.shared.system.QuickStepContract; import java.util.ArrayList; @@ -194,6 +195,8 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset private ImageView mActionsContainerBackground; private HorizontalScrollView mActionsContainer; private LinearLayout mActionsView; + private ScreenshotActionChip mShareChip; + private ScreenshotActionChip mEditChip; private ImageView mBackgroundProtection; private FrameLayout mDismissButton; @@ -215,6 +218,14 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset private int mLeftInset; private int mRightInset; + private ArrayList<ScreenshotActionChip> mSmartChips = new ArrayList<>(); + private PendingInteraction mPendingInteraction; + private enum PendingInteraction { + PREVIEW, + EDIT, + SHARE + } + // standard material ease private final Interpolator mFastOutSlowIn; @@ -234,12 +245,20 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset }; @Inject - public GlobalScreenshot( - Context context, @Main Resources resources, + public GlobalScreenshot(Context context, ScreenshotSmartActions screenshotSmartActions, ScreenshotNotificationsController screenshotNotificationsController, UiEventLogger uiEventLogger) { - mContext = context; + + // Create a visual (Window) context + // After this, our windowToken is available from mContext.getActivityToken() + final DisplayManager dm = context.getSystemService(DisplayManager.class); + mDisplay = dm.getDisplay(DEFAULT_DISPLAY); + Context displayContext = context.createDisplayContext(mDisplay); + mContext = new WindowContext( + displayContext, WindowManager.LayoutParams.TYPE_SCREENSHOT, null); + mWindowManager = mContext.getSystemService(WindowManager.class); + mScreenshotSmartActions = screenshotSmartActions; mNotificationsController = screenshotNotificationsController; mUiEventLogger = uiEventLogger; @@ -263,13 +282,13 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM, PixelFormat.TRANSLUCENT); mWindowLayoutParams.setTitle("ScreenshotAnimation"); - mWindowLayoutParams.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; + mWindowLayoutParams.layoutInDisplayCutoutMode = + WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; mWindowLayoutParams.setFitInsetsTypes(0 /* types */); - mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); - mDisplay = mWindowManager.getDefaultDisplay(); mDisplayMetrics = new DisplayMetrics(); mDisplay.getRealMetrics(mDisplayMetrics); + final Resources resources = mContext.getResources(); mCornerSizeX = resources.getDimensionPixelSize(R.dimen.global_screenshot_x_scale); mDismissDeltaY = resources.getDimensionPixelSize(R.dimen.screenshot_dismissal_height_delta); @@ -548,6 +567,9 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset mOnCompleteRunnable.run(); }); + mShareChip = mActionsContainer.findViewById(R.id.screenshot_share_chip); + mEditChip = mActionsContainer.findViewById(R.id.screenshot_edit_chip); + mScreenshotFlash = mScreenshotLayout.findViewById(R.id.global_screenshot_flash); mScreenshotSelectorView = mScreenshotLayout.findViewById(R.id.global_screenshot_selector); mScreenshotLayout.setFocusable(true); @@ -756,11 +778,11 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset @Override public void onAnimationEnd(Animator animation) { super.onAnimationEnd(animation); - createScreenshotActionsShadeAnimation(imageData).start(); + setChipIntents(imageData); } }); } else { - createScreenshotActionsShadeAnimation(imageData).start(); + setChipIntents(imageData); } }); } @@ -895,19 +917,14 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset mScreenshotAnimatedView.setVisibility(View.GONE); mScreenshotPreview.setVisibility(View.VISIBLE); mScreenshotLayout.forceLayout(); + createScreenshotActionsShadeAnimation().start(); } }); return dropInAnimation; } - private ValueAnimator createScreenshotActionsShadeAnimation(SavedImageData imageData) { - LayoutInflater inflater = LayoutInflater.from(mContext); - mActionsView.removeAllViews(); - mScreenshotLayout.invalidate(); - mScreenshotLayout.requestLayout(); - mScreenshotLayout.getViewTreeObserver().dispatchOnGlobalLayout(); - + private ValueAnimator createScreenshotActionsShadeAnimation() { // By default the activities won't be able to start immediately; override this to keep // the same behavior as if started from a notification try { @@ -917,61 +934,35 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset ArrayList<ScreenshotActionChip> chips = new ArrayList<>(); - for (Notification.Action smartAction : imageData.smartActions) { - ScreenshotActionChip actionChip = (ScreenshotActionChip) inflater.inflate( - R.layout.global_screenshot_action_chip, mActionsView, false); - actionChip.setText(smartAction.title); - actionChip.setIcon(smartAction.getIcon(), false); - actionChip.setPendingIntent(smartAction.actionIntent, - () -> { - mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_SMART_ACTION_TAPPED); - dismissScreenshot("chip tapped", false); - mOnCompleteRunnable.run(); - }); - mActionsView.addView(actionChip); - chips.add(actionChip); - } - - ScreenshotActionChip shareChip = (ScreenshotActionChip) inflater.inflate( - R.layout.global_screenshot_action_chip, mActionsView, false); - shareChip.setText(imageData.shareAction.title); - shareChip.setIcon(imageData.shareAction.getIcon(), true); - shareChip.setPendingIntent(imageData.shareAction.actionIntent, () -> { - mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_SHARE_TAPPED); - dismissScreenshot("chip tapped", false); - mOnCompleteRunnable.run(); + mShareChip.setText(mContext.getString(com.android.internal.R.string.share)); + mShareChip.setIcon(Icon.createWithResource(mContext, R.drawable.ic_screenshot_share), true); + mShareChip.setOnClickListener(v -> { + mShareChip.setIsPending(true); + mEditChip.setIsPending(false); + mPendingInteraction = PendingInteraction.SHARE; }); - mActionsView.addView(shareChip); - chips.add(shareChip); - - ScreenshotActionChip editChip = (ScreenshotActionChip) inflater.inflate( - R.layout.global_screenshot_action_chip, mActionsView, false); - editChip.setText(imageData.editAction.title); - editChip.setIcon(imageData.editAction.getIcon(), true); - editChip.setPendingIntent(imageData.editAction.actionIntent, () -> { - mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_EDIT_TAPPED); - dismissScreenshot("chip tapped", false); - mOnCompleteRunnable.run(); + chips.add(mShareChip); + + mEditChip.setText(mContext.getString(com.android.internal.R.string.screenshot_edit)); + mEditChip.setIcon(Icon.createWithResource(mContext, R.drawable.ic_screenshot_edit), true); + mEditChip.setOnClickListener(v -> { + mEditChip.setIsPending(true); + mShareChip.setIsPending(false); + mPendingInteraction = PendingInteraction.EDIT; }); - mActionsView.addView(editChip); - chips.add(editChip); + chips.add(mEditChip); mScreenshotPreview.setOnClickListener(v -> { - try { - imageData.editAction.actionIntent.send(); - mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_PREVIEW_TAPPED); - dismissScreenshot("screenshot preview tapped", false); - mOnCompleteRunnable.run(); - } catch (PendingIntent.CanceledException e) { - Log.e(TAG, "Intent cancelled", e); - } + mShareChip.setIsPending(false); + mEditChip.setIsPending(false); + mPendingInteraction = PendingInteraction.PREVIEW; }); - mScreenshotPreview.setContentDescription(imageData.editAction.title); // remove the margin from the last chip so that it's correctly aligned with the end LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) - mActionsView.getChildAt(mActionsView.getChildCount() - 1).getLayoutParams(); - params.setMarginEnd(0); + mActionsView.getChildAt(0).getLayoutParams(); + params.setMarginStart(0); + mActionsView.getChildAt(0).setLayoutParams(params); ValueAnimator animator = ValueAnimator.ofFloat(0, 1); animator.setDuration(SCREENSHOT_ACTIONS_EXPANSION_DURATION_MS); @@ -1004,6 +995,63 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset return animator; } + private void setChipIntents(SavedImageData imageData) { + mShareChip.setPendingIntent(imageData.shareAction.actionIntent, () -> { + mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_SHARE_TAPPED); + dismissScreenshot("chip tapped", false); + mOnCompleteRunnable.run(); + }); + + mEditChip.setPendingIntent(imageData.editAction.actionIntent, () -> { + mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_EDIT_TAPPED); + dismissScreenshot("chip tapped", false); + mOnCompleteRunnable.run(); + }); + + mScreenshotPreview.setOnClickListener(v -> { + try { + imageData.editAction.actionIntent.send(); + mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_PREVIEW_TAPPED); + dismissScreenshot("screenshot preview tapped", false); + mOnCompleteRunnable.run(); + } catch (PendingIntent.CanceledException e) { + Log.e(TAG, "Intent cancelled", e); + } + }); + + if (mPendingInteraction != null) { + switch(mPendingInteraction) { + case PREVIEW: + mScreenshotPreview.callOnClick(); + break; + case SHARE: + mShareChip.callOnClick(); + break; + case EDIT: + mEditChip.callOnClick(); + break; + } + } else { + LayoutInflater inflater = LayoutInflater.from(mContext); + + for (Notification.Action smartAction : imageData.smartActions) { + ScreenshotActionChip actionChip = (ScreenshotActionChip) inflater.inflate( + R.layout.global_screenshot_action_chip, mActionsView, false); + actionChip.setText(smartAction.title); + actionChip.setIcon(smartAction.getIcon(), false); + actionChip.setPendingIntent(smartAction.actionIntent, + () -> { + mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_SMART_ACTION_TAPPED); + dismissScreenshot("chip tapped", false); + mOnCompleteRunnable.run(); + }); + actionChip.setAlpha(1); + mActionsView.addView(actionChip); + mSmartChips.add(actionChip); + } + } + } + private AnimatorSet createScreenshotDismissAnimation() { ValueAnimator alphaAnim = ValueAnimator.ofFloat(0, 1); alphaAnim.setStartDelay(SCREENSHOT_DISMISS_ALPHA_OFFSET_MS); @@ -1046,9 +1094,16 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset mDismissButton.setVisibility(View.GONE); mScreenshotPreview.setVisibility(View.GONE); mScreenshotPreview.setLayerType(View.LAYER_TYPE_NONE, null); - mScreenshotPreview.setContentDescription( - mContext.getResources().getString(R.string.screenshot_preview_description)); mScreenshotPreview.setOnClickListener(null); + mShareChip.setOnClickListener(null); + mEditChip.setOnClickListener(null); + mShareChip.setIsPending(false); + mEditChip.setIsPending(false); + mPendingInteraction = null; + for (ScreenshotActionChip chip : mSmartChips) { + mActionsView.removeView(chip); + } + mSmartChips.clear(); mScreenshotLayout.setAlpha(1); mDismissButton.setTranslationY(0); mActionsContainer.setTranslationY(0); @@ -1070,9 +1125,9 @@ public class GlobalScreenshot implements ViewTreeObserver.OnComputeInternalInset */ private void setWindowFocusable(boolean focusable) { if (focusable) { - mWindowLayoutParams.flags &= ~FLAG_NOT_FOCUSABLE; + mWindowLayoutParams.flags &= ~WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; } else { - mWindowLayoutParams.flags |= FLAG_NOT_FOCUSABLE; + mWindowLayoutParams.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; } if (mScreenshotLayout.isAttachedToWindow()) { mWindowManager.updateViewLayout(mScreenshotLayout, mWindowLayoutParams); diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotActionChip.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotActionChip.java index a48870240384..3370946ec274 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotActionChip.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotActionChip.java @@ -16,7 +16,6 @@ package com.android.systemui.screenshot; -import android.annotation.ColorInt; import android.app.PendingIntent; import android.content.Context; import android.graphics.drawable.Icon; @@ -35,9 +34,9 @@ public class ScreenshotActionChip extends FrameLayout { private static final String TAG = "ScreenshotActionChip"; - private ImageView mIcon; - private TextView mText; - private @ColorInt int mIconColor; + private ImageView mIconView; + private TextView mTextView; + private boolean mIsPending = false; public ScreenshotActionChip(Context context) { this(context, null); @@ -54,25 +53,29 @@ public class ScreenshotActionChip extends FrameLayout { public ScreenshotActionChip( Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); - - mIconColor = context.getColor(R.color.global_screenshot_button_icon); } @Override protected void onFinishInflate() { - mIcon = findViewById(R.id.screenshot_action_chip_icon); - mText = findViewById(R.id.screenshot_action_chip_text); + mIconView = findViewById(R.id.screenshot_action_chip_icon); + mTextView = findViewById(R.id.screenshot_action_chip_text); + } + + @Override + public void setPressed(boolean pressed) { + // override pressed state to true if there is an action pending + super.setPressed(mIsPending || pressed); } void setIcon(Icon icon, boolean tint) { - mIcon.setImageIcon(icon); + mIconView.setImageIcon(icon); if (!tint) { - mIcon.setImageTintList(null); + mIconView.setImageTintList(null); } } void setText(CharSequence text) { - mText.setText(text); + mTextView.setText(text); } void setPendingIntent(PendingIntent intent, Runnable finisher) { @@ -85,4 +88,9 @@ public class ScreenshotActionChip extends FrameLayout { } }); } + + void setIsPending(boolean isPending) { + mIsPending = isPending; + setPressed(mIsPending); + } } diff --git a/packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java b/packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java index 1bea72aea2ba..72034f84fd30 100644 --- a/packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java +++ b/packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java @@ -50,6 +50,8 @@ import com.android.systemui.broadcast.BroadcastDispatcher; import java.util.ArrayList; +import javax.inject.Inject; + public class BrightnessController implements ToggleSlider.Listener { private static final String TAG = "StatusBar.BrightnessController"; private static final int SLIDER_ANIMATION_DURATION = 3000; @@ -475,4 +477,20 @@ public class BrightnessController implements ToggleSlider.Listener { mSliderAnimator.start(); } + /** Factory for creating a {@link BrightnessController}. */ + public static class Factory { + private final Context mContext; + private final BroadcastDispatcher mBroadcastDispatcher; + + @Inject + public Factory(Context context, BroadcastDispatcher broadcastDispatcher) { + mContext = context; + mBroadcastDispatcher = broadcastDispatcher; + } + + /** Create a {@link BrightnessController} */ + public BrightnessController create(ToggleSlider toggleSlider) { + return new BrightnessController(mContext, toggleSlider, mBroadcastDispatcher); + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java index 0184fa7a5bfd..dcee9fa9e648 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java @@ -140,6 +140,8 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController< private static final int MSG_SUPPRESS_AMBIENT_DISPLAY = 56 << MSG_SHIFT; private static final int MSG_REQUEST_WINDOW_MAGNIFICATION_CONNECTION = 57 << MSG_SHIFT; private static final int MSG_HANDLE_WINDOW_MANAGER_LOGGING_COMMAND = 58 << MSG_SHIFT; + //TODO(b/169175022) Update name and when feature name is locked. + private static final int MSG_EMERGENCY_ACTION_LAUNCH_GESTURE = 59 << MSG_SHIFT; public static final int FLAG_EXCLUDE_NONE = 0; public static final int FLAG_EXCLUDE_SEARCH_PANEL = 1 << 0; @@ -258,6 +260,11 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController< default void showAssistDisclosure() { } default void startAssist(Bundle args) { } default void onCameraLaunchGestureDetected(int source) { } + + /** + * Notifies SysUI that the emergency action gesture was detected. + */ + default void onEmergencyActionLaunchGestureDetected() { } default void showPictureInPictureMenu() { } default void setTopAppHidesStatusBar(boolean topAppHidesStatusBar) { } @@ -730,6 +737,14 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController< } @Override + public void onEmergencyActionLaunchGestureDetected() { + synchronized (mLock) { + mHandler.removeMessages(MSG_EMERGENCY_ACTION_LAUNCH_GESTURE); + mHandler.obtainMessage(MSG_EMERGENCY_ACTION_LAUNCH_GESTURE).sendToTarget(); + } + } + + @Override public void addQsTile(ComponentName tile) { synchronized (mLock) { mHandler.obtainMessage(MSG_ADD_QS_TILE, tile).sendToTarget(); @@ -1186,6 +1201,10 @@ public class CommandQueue extends IStatusBar.Stub implements CallbackController< mCallbacks.get(i).onCameraLaunchGestureDetected(msg.arg1); } break; + case MSG_EMERGENCY_ACTION_LAUNCH_GESTURE: + for (int i = 0; i < mCallbacks.size(); i++) { + mCallbacks.get(i).onEmergencyActionLaunchGestureDetected(); + } case MSG_SHOW_PICTURE_IN_PICTURE_MENU: for (int i = 0; i < mCallbacks.size(); i++) { mCallbacks.get(i).showPictureInPictureMenu(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/MediaTransferManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/MediaTransferManager.java deleted file mode 100644 index 1b1a51b8a57b..000000000000 --- a/packages/SystemUI/src/com/android/systemui/statusbar/MediaTransferManager.java +++ /dev/null @@ -1,212 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.statusbar; - -import android.content.Context; -import android.content.res.ColorStateList; -import android.graphics.drawable.Drawable; -import android.graphics.drawable.GradientDrawable; -import android.graphics.drawable.RippleDrawable; -import android.service.notification.StatusBarNotification; -import android.util.FeatureFlagUtils; -import android.util.Log; -import android.view.View; -import android.view.ViewGroup; -import android.view.ViewParent; -import android.widget.ImageView; -import android.widget.LinearLayout; -import android.widget.TextView; - -import com.android.settingslib.bluetooth.LocalBluetoothManager; -import com.android.settingslib.media.InfoMediaManager; -import com.android.settingslib.media.LocalMediaManager; -import com.android.settingslib.media.MediaDevice; -import com.android.settingslib.widget.AdaptiveIcon; -import com.android.systemui.Dependency; -import com.android.systemui.media.dialog.MediaOutputDialogFactory; -import com.android.systemui.statusbar.notification.collection.NotificationEntry; -import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; - -import java.util.ArrayList; -import java.util.List; - -/** - * Class for handling MediaTransfer state over a set of notifications. - */ -public class MediaTransferManager { - private final Context mContext; - private final MediaOutputDialogFactory mMediaOutputDialogFactory; - private MediaDevice mDevice; - private List<View> mViews = new ArrayList<>(); - private LocalMediaManager mLocalMediaManager; - - private static final String TAG = "MediaTransferManager"; - - private final View.OnClickListener mOnClickHandler = new View.OnClickListener() { - @Override - public void onClick(View view) { - if (handleMediaTransfer(view)) { - return; - } - } - - private boolean handleMediaTransfer(View view) { - if (view.findViewById(com.android.internal.R.id.media_seamless) == null) { - return false; - } - - ViewParent parent = view.getParent(); - StatusBarNotification statusBarNotification = - getRowForParent(parent).getEntry().getSbn(); - mMediaOutputDialogFactory.create(statusBarNotification.getPackageName(), true); - return true; - } - }; - - private final LocalMediaManager.DeviceCallback mMediaDeviceCallback = - new LocalMediaManager.DeviceCallback() { - @Override - public void onDeviceListUpdate(List<MediaDevice> devices) { - MediaDevice currentDevice = mLocalMediaManager.getCurrentConnectedDevice(); - // Check because this can be called several times while changing devices - if (mDevice == null || !mDevice.equals(currentDevice)) { - mDevice = currentDevice; - updateAllChips(); - } - } - - @Override - public void onSelectedDeviceStateChanged(MediaDevice device, int state) { - if (mDevice == null || !mDevice.equals(device)) { - mDevice = device; - updateAllChips(); - } - } - }; - - public MediaTransferManager(Context context) { - mContext = context; - mMediaOutputDialogFactory = Dependency.get(MediaOutputDialogFactory.class); - LocalBluetoothManager lbm = Dependency.get(LocalBluetoothManager.class); - InfoMediaManager imm = new InfoMediaManager(mContext, null, null, lbm); - mLocalMediaManager = new LocalMediaManager(mContext, lbm, imm, null); - } - - /** - * Mark a view as removed. If no views remain the media device listener will be unregistered. - * @param root - */ - public void setRemoved(View root) { - if (!FeatureFlagUtils.isEnabled(mContext, FeatureFlagUtils.SEAMLESS_TRANSFER) - || mLocalMediaManager == null || root == null) { - return; - } - View view = root.findViewById(com.android.internal.R.id.media_seamless); - if (mViews.remove(view)) { - if (mViews.size() == 0) { - mLocalMediaManager.unregisterCallback(mMediaDeviceCallback); - } - } else { - Log.e(TAG, "Tried to remove unknown view " + view); - } - } - - private ExpandableNotificationRow getRowForParent(ViewParent parent) { - while (parent != null) { - if (parent instanceof ExpandableNotificationRow) { - return ((ExpandableNotificationRow) parent); - } - parent = parent.getParent(); - } - return null; - } - - /** - * apply the action button for MediaTransfer - * - * @param root The parent container of the view. - * @param entry The entry of MediaTransfer action button. - */ - public void applyMediaTransferView(ViewGroup root, NotificationEntry entry) { - if (!FeatureFlagUtils.isEnabled(mContext, FeatureFlagUtils.SEAMLESS_TRANSFER) - || mLocalMediaManager == null || root == null) { - return; - } - - View view = root.findViewById(com.android.internal.R.id.media_seamless); - if (view == null) { - return; - } - - view.setVisibility(View.VISIBLE); - view.setOnClickListener(mOnClickHandler); - if (!mViews.contains(view)) { - mViews.add(view); - if (mViews.size() == 1) { - mLocalMediaManager.registerCallback(mMediaDeviceCallback); - } - } - - // Initial update - mLocalMediaManager.startScan(); - mDevice = mLocalMediaManager.getCurrentConnectedDevice(); - updateChip(view); - } - - private void updateAllChips() { - for (View view : mViews) { - updateChip(view); - } - } - - private void updateChip(View view) { - ExpandableNotificationRow enr = getRowForParent(view.getParent()); - int fgColor = enr.getNotificationHeader().getOriginalIconColor(); - ColorStateList fgTintList = ColorStateList.valueOf(fgColor); - int bgColor = enr.getCurrentBackgroundTint(); - - // Update outline color - LinearLayout viewLayout = (LinearLayout) view; - RippleDrawable bkgDrawable = (RippleDrawable) viewLayout.getBackground(); - GradientDrawable rect = (GradientDrawable) bkgDrawable.getDrawable(0); - rect.setStroke(2, fgColor); - rect.setColor(bgColor); - - ImageView iconView = view.findViewById(com.android.internal.R.id.media_seamless_image); - TextView deviceName = view.findViewById(com.android.internal.R.id.media_seamless_text); - deviceName.setTextColor(fgTintList); - - if (mDevice != null) { - Drawable icon = mDevice.getIcon(); - iconView.setVisibility(View.VISIBLE); - iconView.setImageTintList(fgTintList); - - if (icon instanceof AdaptiveIcon) { - AdaptiveIcon aIcon = (AdaptiveIcon) icon; - aIcon.setBackgroundColor(bgColor); - iconView.setImageDrawable(aIcon); - } else { - iconView.setImageDrawable(icon); - } - deviceName.setText(mDevice.getName()); - } else { - // Reset to default - iconView.setVisibility(View.GONE); - deviceName.setText(com.android.internal.R.string.ext_media_seamless_action); - } - } -} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationHeaderUtil.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationHeaderUtil.java index 670a65f55844..b7f4e67e48d1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationHeaderUtil.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationHeaderUtil.java @@ -68,10 +68,8 @@ public class NotificationHeaderUtil { @Override public void apply(View parent, View view, boolean apply, boolean reset) { NotificationHeaderView header = (NotificationHeaderView) view; - ImageView icon = (ImageView) view.findViewById( - com.android.internal.R.id.icon); - ImageView expand = (ImageView) view.findViewById( - com.android.internal.R.id.expand_button); + ImageView icon = view.findViewById(com.android.internal.R.id.icon); + ImageView expand = view.findViewById(com.android.internal.R.id.expand_button); applyToChild(icon, apply, header.getOriginalIconColor()); applyToChild(expand, apply, header.getOriginalNotificationColor()); } @@ -178,7 +176,7 @@ public class NotificationHeaderUtil { private void sanitizeHeaderViews(ExpandableNotificationRow row) { if (row.isSummaryWithChildren()) { - sanitizeHeader(row.getNotificationHeader()); + sanitizeHeader(row.getNotificationViewWrapper().getNotificationHeader()); return; } final NotificationContentView layout = row.getPrivateLayout(); @@ -275,7 +273,8 @@ public class NotificationHeaderUtil { } public void init() { - mParentView = mParentRow.getNotificationHeader().findViewById(mId); + mParentView = mParentRow.getNotificationViewWrapper().getNotificationHeader() + .findViewById(mId); mParentData = mExtractor == null ? null : mExtractor.extractData(mParentRow); mApply = !mComparator.isEmpty(mParentView); } @@ -305,7 +304,7 @@ public class NotificationHeaderUtil { public void apply(ExpandableNotificationRow row, boolean reset) { boolean apply = mApply && !reset; if (row.isSummaryWithChildren()) { - applyToView(apply, reset, row.getNotificationHeader()); + applyToView(apply, reset, row.getNotificationViewWrapper().getNotificationHeader()); return; } applyToView(apply, reset, row.getPrivateLayout().getContractedChild()); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ViewTransformationHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/ViewTransformationHelper.java index 83e51cd43ed2..7ecdc812cbd3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/ViewTransformationHelper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/ViewTransformationHelper.java @@ -106,12 +106,8 @@ public class ViewTransformationHelper implements TransformableView, mViewTransformationAnimation.cancel(); } mViewTransformationAnimation = ValueAnimator.ofFloat(0.0f, 1.0f); - mViewTransformationAnimation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { - @Override - public void onAnimationUpdate(ValueAnimator animation) { - transformTo(notification, animation.getAnimatedFraction()); - } - }); + mViewTransformationAnimation.addUpdateListener( + animation -> transformTo(notification, animation.getAnimatedFraction())); mViewTransformationAnimation.setInterpolator(Interpolators.LINEAR); mViewTransformationAnimation.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD); mViewTransformationAnimation.addListener(new AnimatorListenerAdapter() { @@ -167,12 +163,8 @@ public class ViewTransformationHelper implements TransformableView, mViewTransformationAnimation.cancel(); } mViewTransformationAnimation = ValueAnimator.ofFloat(0.0f, 1.0f); - mViewTransformationAnimation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { - @Override - public void onAnimationUpdate(ValueAnimator animation) { - transformFrom(notification, animation.getAnimatedFraction()); - } - }); + mViewTransformationAnimation.addUpdateListener( + animation -> transformFrom(notification, animation.getAnimatedFraction())); mViewTransformationAnimation.addListener(new AnimatorListenerAdapter() { public boolean mCancelled; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java index e1e77b0723a4..7c3b791aed09 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java @@ -618,6 +618,7 @@ public class NotificationEntryManager implements NotificationEntry entry = mPendingNotifications.get(key); if (entry != null) { entry.setSbn(notification); + entry.setRanking(ranking); } else { entry = new NotificationEntry( notification, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java index 133ddfebe84f..6da4d8b70944 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java @@ -41,8 +41,6 @@ import javax.inject.Inject; */ @SysUISingleton public class RankingCoordinator implements Coordinator { - private static final String TAG = "RankingNotificationCoordinator"; - private final StatusBarStateController mStatusBarStateController; private final HighPriorityProvider mHighPriorityProvider; private final NodeController mSilentHeaderController; @@ -65,7 +63,7 @@ public class RankingCoordinator implements Coordinator { mStatusBarStateController.addCallback(mStatusBarStateCallback); pipeline.addPreGroupFilter(mSuspendedFilter); - pipeline.addPreGroupFilter(mDozingFilter); + pipeline.addPreGroupFilter(mDndVisualEffectsFilter); } public NotifSectioner getAlertingSectioner() { @@ -114,10 +112,10 @@ public class RankingCoordinator implements Coordinator { } }; - private final NotifFilter mDozingFilter = new NotifFilter("IsDozingFilter") { + private final NotifFilter mDndVisualEffectsFilter = new NotifFilter( + "DndSuppressingVisualEffects") { @Override public boolean shouldFilterOut(NotificationEntry entry, long now) { - // Dozing + DND Settings from Ranking object if (mStatusBarStateController.isDozing() && entry.shouldSuppressAmbient()) { return true; } @@ -130,7 +128,7 @@ public class RankingCoordinator implements Coordinator { new StatusBarStateController.StateListener() { @Override public void onDozingChanged(boolean isDozing) { - mDozingFilter.invalidateList(); + mDndVisualEffectsFilter.invalidateList(); } }; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java index f1727ec91c72..094e8661d262 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java @@ -124,6 +124,7 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView private float mAppearAnimationTranslation; private int mNormalColor; private boolean mIsBelowSpeedBump; + private long mLastActionUpTime; private float mNormalBackgroundVisibilityAmount; private float mDimmedBackgroundFadeInAmount = -1; @@ -225,6 +226,22 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView return super.onInterceptTouchEvent(ev); } + /** Sets the last action up time this view was touched. */ + void setLastActionUpTime(long eventTime) { + mLastActionUpTime = eventTime; + } + + /** + * Returns the last action up time. The last time will also be cleared because the source of + * action is not only from touch event. That prevents the caller from utilizing the time with + * unrelated event. The time can be 0 if the event is unavailable. + */ + public long getAndResetLastActionUpTime() { + long lastActionUpTime = mLastActionUpTime; + mLastActionUpTime = 0; + return lastActionUpTime; + } + protected boolean disallowSingleClick(MotionEvent ev) { return false; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationViewController.java index dd30c890e75b..41ce51c2762f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationViewController.java @@ -16,6 +16,7 @@ package com.android.systemui.statusbar.notification.row; +import android.os.SystemClock; import android.view.MotionEvent; import android.view.View; import android.view.accessibility.AccessibilityManager; @@ -92,6 +93,9 @@ public class ActivatableNotificationViewController { mBlockNextTouch = false; return true; } + if (ev.getAction() == MotionEvent.ACTION_UP) { + mView.setLastActionUpTime(SystemClock.uptimeMillis()); + } if (mNeedsDimming && !mAccessibilityManager.isTouchExplorationEnabled() && mView.isInteractive()) { if (mNeedsDimming && !mView.isDimmed()) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java index d8d412bf2d41..f788dfe47a61 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java @@ -304,7 +304,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView } } }; - private boolean mForceUnlocked; private boolean mKeepInParent; private boolean mRemoved; private static final Property<ExpandableNotificationRow, Float> TRANSLATE_CONTENT = @@ -410,8 +409,14 @@ public class ExpandableNotificationRow extends ActivatableNotificationView setIconAnimationRunning(running, l); } if (mIsSummaryWithChildren) { - setIconAnimationRunningForChild(running, mChildrenContainer.getHeaderView()); - setIconAnimationRunningForChild(running, mChildrenContainer.getLowPriorityHeaderView()); + NotificationViewWrapper viewWrapper = mChildrenContainer.getNotificationViewWrapper(); + if (viewWrapper != null) { + setIconAnimationRunningForChild(running, viewWrapper.getIcon()); + } + NotificationViewWrapper lowPriWrapper = mChildrenContainer.getLowPriorityViewWrapper(); + if (lowPriWrapper != null) { + setIconAnimationRunningForChild(running, lowPriWrapper.getIcon()); + } List<ExpandableNotificationRow> notificationChildren = mChildrenContainer.getAttachedChildren(); for (int i = 0; i < notificationChildren.size(); i++) { @@ -435,10 +440,9 @@ public class ExpandableNotificationRow extends ActivatableNotificationView private void setIconAnimationRunningForChild(boolean running, View child) { if (child != null) { - ImageView icon = (ImageView) child.findViewById(com.android.internal.R.id.icon); + ImageView icon = child.findViewById(com.android.internal.R.id.icon); setIconRunning(icon, running); - ImageView rightIcon = (ImageView) child.findViewById( - com.android.internal.R.id.right_icon); + ImageView rightIcon = child.findViewById(com.android.internal.R.id.right_icon); setIconRunning(rightIcon, running); } } @@ -497,7 +501,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView /** * Returns whether this row is considered non-blockable (i.e. it's a non-blockable system notif - * or is in a whitelist). + * or is in an allowList). */ public boolean getIsNonblockable() { // If the SystemNotifAsyncTask hasn't finished running or retrieved a value, we'll try once @@ -594,7 +598,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView public int getOriginalIconColor() { if (mIsSummaryWithChildren && !shouldShowPublic()) { - return mChildrenContainer.getVisibleHeader().getOriginalIconColor(); + return mChildrenContainer.getVisibleWrapper().getOriginalIconColor(); } int color = getShowingLayout().getOriginalIconColor(); if (color != Notification.COLOR_INVALID) { @@ -1040,22 +1044,25 @@ public class ExpandableNotificationRow extends ActivatableNotificationView } } - public NotificationHeaderView getNotificationHeader() { + /** + * @return the main notification view wrapper. + */ + public NotificationViewWrapper getNotificationViewWrapper() { if (mIsSummaryWithChildren) { - return mChildrenContainer.getHeaderView(); + return mChildrenContainer.getNotificationViewWrapper(); } - return mPrivateLayout.getNotificationHeader(); + return mPrivateLayout.getNotificationViewWrapper(); } /** - * @return the currently visible notification header. This can be different from - * {@link #getNotificationHeader()} in case it is a low-priority group. + * @return the currently visible notification view wrapper. This can be different from + * {@link #getNotificationViewWrapper()} in case it is a low-priority group. */ - public NotificationHeaderView getVisibleNotificationHeader() { + public NotificationViewWrapper getVisibleNotificationViewWrapper() { if (mIsSummaryWithChildren && !shouldShowPublic()) { - return mChildrenContainer.getVisibleHeader(); + return mChildrenContainer.getVisibleWrapper(); } - return getShowingLayout().getVisibleNotificationHeader(); + return getShowingLayout().getVisibleWrapper(); } public void setLongPressListener(LongPressListener longPressListener) { @@ -1294,16 +1301,6 @@ public class ExpandableNotificationRow extends ActivatableNotificationView onAttachedChildrenCountChanged(); } - public void setForceUnlocked(boolean forceUnlocked) { - mForceUnlocked = forceUnlocked; - if (mIsSummaryWithChildren) { - List<ExpandableNotificationRow> notificationChildren = getAttachedChildren(); - for (ExpandableNotificationRow child : notificationChildren) { - child.setForceUnlocked(forceUnlocked); - } - } - } - @Override public void dismiss(boolean refocusOnDismiss) { super.dismiss(refocusOnDismiss); @@ -1422,7 +1419,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView @Override public View getShelfTransformationTarget() { if (mIsSummaryWithChildren && !shouldShowPublic()) { - return mChildrenContainer.getVisibleHeader().getIcon(); + return mChildrenContainer.getVisibleWrapper().getShelfTransformationTarget(); } return getShowingLayout().getShelfTransformationTarget(); } @@ -1693,37 +1690,30 @@ public class ExpandableNotificationRow extends ActivatableNotificationView @Override protected void onFinishInflate() { super.onFinishInflate(); - mPublicLayout = (NotificationContentView) findViewById(R.id.expandedPublic); - mPrivateLayout = (NotificationContentView) findViewById(R.id.expanded); + mPublicLayout = findViewById(R.id.expandedPublic); + mPrivateLayout = findViewById(R.id.expanded); mLayouts = new NotificationContentView[] {mPrivateLayout, mPublicLayout}; for (NotificationContentView l : mLayouts) { l.setExpandClickListener(mExpandClickListener); l.setContainingNotification(this); } - mGutsStub = (ViewStub) findViewById(R.id.notification_guts_stub); - mGutsStub.setOnInflateListener(new ViewStub.OnInflateListener() { - @Override - public void onInflate(ViewStub stub, View inflated) { - mGuts = (NotificationGuts) inflated; - mGuts.setClipTopAmount(getClipTopAmount()); - mGuts.setActualHeight(getActualHeight()); - mGutsStub = null; - } + mGutsStub = findViewById(R.id.notification_guts_stub); + mGutsStub.setOnInflateListener((stub, inflated) -> { + mGuts = (NotificationGuts) inflated; + mGuts.setClipTopAmount(getClipTopAmount()); + mGuts.setActualHeight(getActualHeight()); + mGutsStub = null; }); - mChildrenContainerStub = (ViewStub) findViewById(R.id.child_container_stub); - mChildrenContainerStub.setOnInflateListener(new ViewStub.OnInflateListener() { + mChildrenContainerStub = findViewById(R.id.child_container_stub); + mChildrenContainerStub.setOnInflateListener((stub, inflated) -> { + mChildrenContainer = (NotificationChildrenContainer) inflated; + mChildrenContainer.setIsLowPriority(mIsLowPriority); + mChildrenContainer.setContainingNotification(ExpandableNotificationRow.this); + mChildrenContainer.onNotificationUpdated(); - @Override - public void onInflate(ViewStub stub, View inflated) { - mChildrenContainer = (NotificationChildrenContainer) inflated; - mChildrenContainer.setIsLowPriority(mIsLowPriority); - mChildrenContainer.setContainingNotification(ExpandableNotificationRow.this); - mChildrenContainer.onNotificationUpdated(); - - if (mShouldTranslateContents) { - mTranslateableViews.add(mChildrenContainer); - } + if (mShouldTranslateContents) { + mTranslateableViews.add(mChildrenContainer); } }); @@ -2183,7 +2173,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView } public boolean isUserLocked() { - return mUserLocked && !mForceUnlocked; + return mUserLocked; } public void setUserLocked(boolean userLocked) { @@ -2303,8 +2293,12 @@ public class ExpandableNotificationRow extends ActivatableNotificationView private void onAttachedChildrenCountChanged() { mIsSummaryWithChildren = mChildrenContainer != null && mChildrenContainer.getNotificationChildCount() > 0; - if (mIsSummaryWithChildren && mChildrenContainer.getHeaderView() == null) { - mChildrenContainer.recreateNotificationHeader(mExpandClickListener, isConversation()); + if (mIsSummaryWithChildren) { + NotificationViewWrapper wrapper = mChildrenContainer.getNotificationViewWrapper(); + if (wrapper == null || wrapper.getNotificationHeader() == null) { + mChildrenContainer.recreateNotificationHeader(mExpandClickListener, + isConversation()); + } } getShowingLayout().updateBackgroundColor(false /* animate */); mPrivateLayout.updateExpandButtons(isExpandable()); @@ -2414,9 +2408,9 @@ public class ExpandableNotificationRow extends ActivatableNotificationView * the top. */ private void updateContentShiftHeight() { - NotificationHeaderView notificationHeader = getVisibleNotificationHeader(); - if (notificationHeader != null) { - CachingIconView icon = notificationHeader.getIcon(); + NotificationViewWrapper wrapper = getVisibleNotificationViewWrapper(); + CachingIconView icon = wrapper == null ? null : wrapper.getIcon(); + if (icon != null) { mIconTransformContentShift = getRelativeTopPadding(icon) + icon.getHeight(); } else { mIconTransformContentShift = mContentShift; @@ -2504,12 +2498,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView .alpha(0f) .setStartDelay(delay) .setDuration(duration) - .withEndAction(new Runnable() { - @Override - public void run() { - hiddenView.setVisibility(View.INVISIBLE); - } - }); + .withEndAction(() -> hiddenView.setVisibility(View.INVISIBLE)); } for (View showView : shownChildren) { showView.setVisibility(View.VISIBLE); @@ -2867,7 +2856,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView } float x = event.getX(); float y = event.getY(); - NotificationHeaderView header = getVisibleNotificationHeader(); + NotificationHeaderView header = getVisibleNotificationViewWrapper().getNotificationHeader(); if (header != null && header.isInTouchRect(x - getTranslation(), y)) { return true; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java index 8a644ed4d3ff..160b6f78d9a3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java @@ -44,7 +44,6 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.ContrastColorUtil; import com.android.systemui.Dependency; import com.android.systemui.R; -import com.android.systemui.statusbar.MediaTransferManager; import com.android.systemui.statusbar.RemoteInputController; import com.android.systemui.statusbar.SmartReplyController; import com.android.systemui.statusbar.TransformableView; @@ -173,12 +172,10 @@ public class NotificationContentView extends FrameLayout { private boolean mIsContentExpandable; private boolean mRemoteInputVisible; private int mUnrestrictedContentHeight; - private MediaTransferManager mMediaTransferManager; public NotificationContentView(Context context, AttributeSet attrs) { super(context, attrs); mHybridGroupManager = new HybridGroupManager(getContext()); - mMediaTransferManager = new MediaTransferManager(getContext()); mSmartReplyConstants = Dependency.get(SmartReplyConstants.class); mSmartReplyController = Dependency.get(SmartReplyController.class); initView(); @@ -1021,6 +1018,10 @@ public class NotificationContentView extends FrameLayout { mSingleLineView }; } + public NotificationViewWrapper getVisibleWrapper() { + return getVisibleWrapper(mVisibleType); + } + public NotificationViewWrapper getVisibleWrapper(int visibleType) { switch (visibleType) { case VISIBLE_TYPE_EXPANDED: @@ -1157,7 +1158,6 @@ public class NotificationContentView extends FrameLayout { mHeadsUpWrapper.onContentUpdated(row); } applyRemoteInputAndSmartReply(entry); - applyMediaTransfer(entry); updateLegacy(); mForceSelectNextLayout = true; mPreviousExpandedRemoteInputIntent = null; @@ -1185,22 +1185,6 @@ public class NotificationContentView extends FrameLayout { } } - private void applyMediaTransfer(final NotificationEntry entry) { - if (!entry.isMediaNotification()) { - return; - } - - View bigContentView = mExpandedChild; - if (bigContentView != null && (bigContentView instanceof ViewGroup)) { - mMediaTransferManager.applyMediaTransferView((ViewGroup) bigContentView, entry); - } - - View smallContentView = mContractedChild; - if (smallContentView != null && (smallContentView instanceof ViewGroup)) { - mMediaTransferManager.applyMediaTransferView((ViewGroup) smallContentView, entry); - } - } - /** * Returns whether the {@link Notification} represented by entry has a free-form remote input. * Such an input can be used e.g. to implement smart reply buttons - by passing the replies @@ -1561,18 +1545,20 @@ public class NotificationContentView extends FrameLayout { mIsContentExpandable = expandable; } - public NotificationHeaderView getNotificationHeader() { - NotificationHeaderView header = null; - if (mContractedChild != null) { - header = mContractedWrapper.getNotificationHeader(); + /** + * @return a view wrapper for one of the inflated states of the notification. + */ + public NotificationViewWrapper getNotificationViewWrapper() { + if (mContractedChild != null && mContractedWrapper != null) { + return mContractedWrapper; } - if (header == null && mExpandedChild != null) { - header = mExpandedWrapper.getNotificationHeader(); + if (mExpandedChild != null && mExpandedWrapper != null) { + return mExpandedWrapper; } - if (header == null && mHeadsUpChild != null) { - header = mHeadsUpWrapper.getNotificationHeader(); + if (mHeadsUpChild != null && mHeadsUpWrapper != null) { + return mHeadsUpWrapper; } - return header; + return null; } public void showFeedbackIcon(boolean show) { @@ -1600,11 +1586,6 @@ public class NotificationContentView extends FrameLayout { } } - public NotificationHeaderView getVisibleNotificationHeader() { - NotificationViewWrapper wrapper = getVisibleWrapper(mVisibleType); - return wrapper == null ? null : wrapper.getNotificationHeader(); - } - public void setContainingNotification(ExpandableNotificationRow containingNotification) { mContainingNotification = containingNotification; } @@ -1662,11 +1643,9 @@ public class NotificationContentView extends FrameLayout { } if (mExpandedWrapper != null) { mExpandedWrapper.setRemoved(); - mMediaTransferManager.setRemoved(mExpandedChild); } if (mContractedWrapper != null) { mContractedWrapper.setRemoved(); - mMediaTransferManager.setRemoved(mContractedChild); } if (mHeadsUpWrapper != null) { mHeadsUpWrapper.setRemoved(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigTextTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigTextTemplateViewWrapper.java index 41f93cceacc7..d58c183f27e3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigTextTemplateViewWrapper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationBigTextTemplateViewWrapper.java @@ -37,7 +37,7 @@ public class NotificationBigTextTemplateViewWrapper extends NotificationTemplate } private void resolveViews(StatusBarNotification notification) { - mBigtext = (ImageFloatingTextView) mView.findViewById(com.android.internal.R.id.big_text); + mBigtext = mView.findViewById(com.android.internal.R.id.big_text); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java index 3f5867477f16..8314aff99c65 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java @@ -55,8 +55,6 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper { protected final ViewTransformationHelper mTransformationHelper; - protected int mColor; - private CachingIconView mIcon; private NotificationExpandButton mExpandButton; protected NotificationHeaderView mNotificationHeader; @@ -119,7 +117,6 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper { mFeedbackIcon = mView.findViewById(com.android.internal.R.id.feedback); if (mNotificationHeader != null) { mNotificationHeader.setShowExpandButtonAtEnd(mShowExpandButtonAtEnd); - mColor = mNotificationHeader.getOriginalIconColor(); } } @@ -296,6 +293,11 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper { } @Override + public CachingIconView getIcon() { + return mIcon; + } + + @Override public int getOriginalIconColor() { return mIcon.getOriginalIconColor(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapper.java index 33c939054be5..2535e5ddc3d1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationMediaTemplateViewWrapper.java @@ -370,7 +370,7 @@ public class NotificationMediaTemplateViewWrapper extends NotificationTemplateVi return; } - int tintColor = getNotificationHeader().getOriginalIconColor(); + int tintColor = getOriginalIconColor(); mSeekBarElapsedTime.setTextColor(tintColor); mSeekBarTotalTime.setTextColor(tintColor); mSeekBarTotalTime.setShadowLayer(1.5f, 1.5f, 1.5f, mBackgroundColor); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapper.java index 14aab9d62dfd..76ec59e0ec28 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationTemplateViewWrapper.java @@ -140,13 +140,13 @@ public class NotificationTemplateViewWrapper extends NotificationHeaderViewWrapp } private void resolveTemplateViews(StatusBarNotification notification) { - mPicture = (ImageView) mView.findViewById(com.android.internal.R.id.right_icon); + mPicture = mView.findViewById(com.android.internal.R.id.right_icon); if (mPicture != null) { mPicture.setTag(ImageTransformState.ICON_TAG, notification.getNotification().getLargeIcon()); } - mTitle = (TextView) mView.findViewById(com.android.internal.R.id.title); - mText = (TextView) mView.findViewById(com.android.internal.R.id.text); + mTitle = mView.findViewById(com.android.internal.R.id.title); + mText = mView.findViewById(com.android.internal.R.id.text); final View progress = mView.findViewById(com.android.internal.R.id.progress); if (progress instanceof ProgressBar) { mProgressBar = (ProgressBar) progress; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java index 42f5e389d5a8..e2f0799a5c35 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationViewWrapper.java @@ -29,7 +29,6 @@ import android.graphics.Rect; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.os.Build; -import android.util.ArraySet; import android.view.NotificationHeaderView; import android.view.View; import android.view.ViewGroup; @@ -38,7 +37,7 @@ import android.widget.TextView; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.graphics.ColorUtils; import com.android.internal.util.ContrastColorUtil; -import com.android.internal.widget.ConversationLayout; +import com.android.internal.widget.CachingIconView; import com.android.systemui.statusbar.CrossFadeHelper; import com.android.systemui.statusbar.TransformableView; import com.android.systemui.statusbar.notification.TransformState; @@ -67,8 +66,7 @@ public abstract class NotificationViewWrapper implements TransformableView { } else if ("messaging".equals(v.getTag())) { return new NotificationMessagingTemplateViewWrapper(ctx, v, row); } else if ("conversation".equals(v.getTag())) { - return new NotificationConversationTemplateViewWrapper(ctx, (ConversationLayout) v, - row); + return new NotificationConversationTemplateViewWrapper(ctx, v, row); } Class<? extends Notification.Style> style = row.getEntry().getSbn().getNotification().getNotificationStyle(); @@ -241,7 +239,16 @@ public abstract class NotificationViewWrapper implements TransformableView { /** * @return the expand button if it exists */ - public @Nullable View getExpandButton() { + @Nullable + public View getExpandButton() { + return null; + } + + /** + * @return the icon if it exists + */ + @Nullable + public CachingIconView getIcon() { return null; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java index a396305a49b6..a5d51c64d4ee 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java @@ -31,6 +31,7 @@ import android.widget.RemoteViews; import android.widget.TextView; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.widget.CachingIconView; import com.android.systemui.R; import com.android.systemui.statusbar.CrossFadeHelper; import com.android.systemui.statusbar.NotificationHeaderUtil; @@ -318,9 +319,8 @@ public class NotificationChildrenContainer extends ViewGroup { RemoteViews header = builder.makeNotificationHeader(); if (mNotificationHeader == null) { mNotificationHeader = (NotificationHeaderView) header.apply(getContext(), this); - final View expandButton = mNotificationHeader.findViewById( - com.android.internal.R.id.expand_button); - expandButton.setVisibility(VISIBLE); + mNotificationHeader.findViewById(com.android.internal.R.id.expand_button) + .setVisibility(VISIBLE); mNotificationHeader.setOnClickListener(mHeaderClickListener); mNotificationHeaderWrapper = NotificationViewWrapper.wrap(getContext(), mNotificationHeader, mContainingNotification); @@ -361,9 +361,8 @@ public class NotificationChildrenContainer extends ViewGroup { if (mNotificationHeaderLowPriority == null) { mNotificationHeaderLowPriority = (NotificationHeaderView) header.apply(getContext(), this); - final View expandButton = mNotificationHeaderLowPriority.findViewById( - com.android.internal.R.id.expand_button); - expandButton.setVisibility(VISIBLE); + mNotificationHeaderLowPriority.findViewById(com.android.internal.R.id.expand_button) + .setVisibility(VISIBLE); mNotificationHeaderLowPriority.setOnClickListener(mHeaderClickListener); mNotificationHeaderWrapperLowPriority = NotificationViewWrapper.wrap(getContext(), mNotificationHeaderLowPriority, mContainingNotification); @@ -869,12 +868,12 @@ public class NotificationChildrenContainer extends ViewGroup { return mContainingNotification; } - public NotificationHeaderView getHeaderView() { - return mNotificationHeader; + public NotificationViewWrapper getNotificationViewWrapper() { + return mNotificationHeaderWrapper; } - public NotificationHeaderView getLowPriorityHeaderView() { - return mNotificationHeaderLowPriority; + public NotificationViewWrapper getLowPriorityViewWrapper() { + return mNotificationHeaderWrapperLowPriority; } @VisibleForTesting @@ -1224,16 +1223,15 @@ public class NotificationChildrenContainer extends ViewGroup { public void setShelfIconVisible(boolean iconVisible) { if (mNotificationHeaderWrapper != null) { - NotificationHeaderView header = mNotificationHeaderWrapper.getNotificationHeader(); - if (header != null) { - header.getIcon().setForceHidden(iconVisible); + CachingIconView icon = mNotificationHeaderWrapper.getIcon(); + if (icon != null) { + icon.setForceHidden(iconVisible); } } if (mNotificationHeaderWrapperLowPriority != null) { - NotificationHeaderView header - = mNotificationHeaderWrapperLowPriority.getNotificationHeader(); - if (header != null) { - header.getIcon().setForceHidden(iconVisible); + CachingIconView icon = mNotificationHeaderWrapperLowPriority.getIcon(); + if (icon != null) { + icon.setForceHidden(iconVisible); } } } @@ -1254,12 +1252,14 @@ public class NotificationChildrenContainer extends ViewGroup { } } - public NotificationHeaderView getVisibleHeader() { - NotificationHeaderView header = mNotificationHeader; + /** + * @return the view wrapper for the currently showing priority. + */ + public NotificationViewWrapper getVisibleWrapper() { if (showingAsLowPriority()) { - header = mNotificationHeaderLowPriority; + return mNotificationHeaderWrapperLowPriority; } - return header; + return mNotificationHeaderWrapper; } public void onExpansionChanged() { 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 af6ac223ada1..2767b7e1627d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java @@ -38,9 +38,9 @@ import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.KeyguardUpdateMonitorCallback; import com.android.keyguard.ViewMediatorCallback; import com.android.keyguard.dagger.KeyguardBouncerComponent; -import com.android.keyguard.dagger.RootView; import com.android.systemui.DejankUtils; import com.android.systemui.biometrics.AuthController; +import com.android.systemui.dagger.qualifiers.RootView; import com.android.systemui.keyguard.DismissCallbackRegistry; import com.android.systemui.plugins.FalsingManager; import com.android.systemui.shared.system.SysUiStatsLog; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java index 1fdf631a858d..ef4108b9762d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java @@ -175,8 +175,8 @@ public class KeyguardClockPositionAlgorithm { } /** - * Update lock screen mode for testing different layouts - */ + * Update lock screen mode for testing different layouts + */ public void onLockScreenModeChanged(int mode) { mLockScreenMode = mode; } @@ -241,6 +241,13 @@ public class KeyguardClockPositionAlgorithm { clockYDark = MathUtils.lerp(clockYBouncer, clockYDark, shadeExpansion); float darkAmount = mBypassEnabled && !mHasCustomClock ? 1.0f : mDarkAmount; + + // TODO(b/12836565) - prototyping only adjustment + if (mLockScreenMode != KeyguardUpdateMonitor.LOCK_SCREEN_MODE_NORMAL) { + // This will keep the clock at the top for AOD + darkAmount = 0f; + } + return (int) (MathUtils.lerp(clockY, clockYDark, darkAmount) + mEmptyDragAmount); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java index 86d4ac1cb443..47b16c8eaaa5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java @@ -70,6 +70,7 @@ import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.util.LatencyTracker; import com.android.keyguard.KeyguardClockSwitchController; import com.android.keyguard.KeyguardStatusView; +import com.android.keyguard.KeyguardStatusViewController; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.KeyguardUpdateMonitorCallback; import com.android.keyguard.dagger.KeyguardStatusViewComponent; @@ -202,9 +203,6 @@ public class NotificationPanelViewController extends PanelViewController { private static final Rect M_DUMMY_DIRTY_RECT = new Rect(0, 0, 1, 1); private static final Rect EMPTY_RECT = new Rect(); - private static final AnimationProperties - CLOCK_ANIMATION_PROPERTIES = - new AnimationProperties().setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD); private final AnimatableProperty KEYGUARD_HEADS_UP_SHOWING_AMOUNT = AnimatableProperty.from( "KEYGUARD_HEADS_UP_SHOWING_AMOUNT", (notificationPanelView, aFloat) -> setKeyguardHeadsUpShowingAmount(aFloat), @@ -280,7 +278,7 @@ public class NotificationPanelViewController extends PanelViewController { private ViewGroup mBigClockContainer; private QS mQs; private FrameLayout mQsFrame; - private KeyguardStatusView mKeyguardStatusView; + private KeyguardStatusViewController mKeyguardStatusViewController; private View mQsNavbarScrim; private NotificationsQuickSettingsContainer mNotificationContainerParent; private boolean mAnimateNextPositionUpdate; @@ -344,7 +342,6 @@ public class NotificationPanelViewController extends PanelViewController { private boolean mIsLaunchTransitionRunning; private Runnable mLaunchAnimationEndRunnable; private boolean mOnlyAffordanceInThisMotion; - private boolean mKeyguardStatusViewAnimating; private ValueAnimator mQsSizeChangeAnimator; private boolean mQsScrimEnabled = true; @@ -606,16 +603,8 @@ public class NotificationPanelViewController extends PanelViewController { private void onFinishInflate() { loadDimens(); mKeyguardStatusBar = mView.findViewById(R.id.keyguard_header); - mKeyguardStatusView = mView.findViewById(R.id.keyguard_status_view); - - KeyguardClockSwitchController keyguardClockSwitchController = - mKeyguardStatusViewComponentFactory - .build(mKeyguardStatusView) - .getKeyguardClockSwitchController(); - keyguardClockSwitchController.init(); mBigClockContainer = mView.findViewById(R.id.big_clock_container); - keyguardClockSwitchController.setBigClockContainer(mBigClockContainer); - + updateViewControllers(mView.findViewById(R.id.keyguard_status_view)); mNotificationContainerParent = mView.findViewById(R.id.notification_container_parent); NotificationStackScrollLayout stackScrollLayout = mView.findViewById( R.id.notification_stack_scroller); @@ -689,11 +678,24 @@ public class NotificationPanelViewController extends PanelViewController { R.dimen.heads_up_status_bar_padding); } + private void updateViewControllers(KeyguardStatusView keyguardStatusView) { + // Re-associate the KeyguardStatusViewController + KeyguardStatusViewComponent statusViewComponent = + mKeyguardStatusViewComponentFactory.build(keyguardStatusView); + mKeyguardStatusViewController = statusViewComponent.getKeyguardStatusViewController(); + mKeyguardStatusViewController.init(); + + // Re-associate the clock container with the keyguard clock switch. + KeyguardClockSwitchController keyguardClockSwitchController = + statusViewComponent.getKeyguardClockSwitchController(); + keyguardClockSwitchController.setBigClockContainer(mBigClockContainer); + } + /** * Returns if there's a custom clock being presented. */ public boolean hasCustomClock() { - return mKeyguardStatusView.hasCustomClock(); + return mKeyguardStatusViewController.hasCustomClock(); } private void setStatusBar(StatusBar bar) { @@ -730,21 +732,16 @@ public class NotificationPanelViewController extends PanelViewController { private void reInflateViews() { // Re-inflate the status view group. - int index = mView.indexOfChild(mKeyguardStatusView); - mView.removeView(mKeyguardStatusView); - mKeyguardStatusView = (KeyguardStatusView) mInjectionInflationController.injectable( + KeyguardStatusView keyguardStatusView = mView.findViewById(R.id.keyguard_status_view); + int index = mView.indexOfChild(keyguardStatusView); + mView.removeView(keyguardStatusView); + keyguardStatusView = (KeyguardStatusView) mInjectionInflationController.injectable( LayoutInflater.from(mView.getContext())).inflate( R.layout.keyguard_status_view, mView, false); - mView.addView(mKeyguardStatusView, index); + mView.addView(keyguardStatusView, index); - // Re-associate the clock container with the keyguard clock switch. mBigClockContainer.removeAllViews(); - KeyguardClockSwitchController keyguardClockSwitchController = - mKeyguardStatusViewComponentFactory - .build(mKeyguardStatusView) - .getKeyguardClockSwitchController(); - keyguardClockSwitchController.init(); - keyguardClockSwitchController.setBigClockContainer(mBigClockContainer); + updateViewControllers(keyguardStatusView); // Update keyguard bottom area index = mView.indexOfChild(mKeyguardBottomArea); @@ -764,7 +761,11 @@ public class NotificationPanelViewController extends PanelViewController { mKeyguardStatusBar.onThemeChanged(); } - setKeyguardStatusViewVisibility(mBarState, false, false); + mKeyguardStatusViewController.setKeyguardStatusViewVisibility( + mBarState, + false, + false, + mBarState); setKeyguardBottomAreaVisibility(mBarState, false); if (mOnReinflationListener != null) { mOnReinflationListener.run(); @@ -858,23 +859,23 @@ public class NotificationPanelViewController extends PanelViewController { } else { int totalHeight = mView.getHeight(); int bottomPadding = Math.max(mIndicationBottomPadding, mAmbientIndicationBottomPadding); - int clockPreferredY = mKeyguardStatusView.getClockPreferredY(totalHeight); + int clockPreferredY = mKeyguardStatusViewController.getClockPreferredY(totalHeight); boolean bypassEnabled = mKeyguardBypassController.getBypassEnabled(); final boolean hasVisibleNotifications = !bypassEnabled && mNotificationStackScrollLayoutController.getVisibleNotificationCount() != 0; - mKeyguardStatusView.setHasVisibleNotifications(hasVisibleNotifications); + mKeyguardStatusViewController.setHasVisibleNotifications(hasVisibleNotifications); mClockPositionAlgorithm.setup(mStatusBarMinHeight, totalHeight - bottomPadding, mNotificationStackScrollLayoutController.getIntrinsicContentHeight(), getExpandedFraction(), - totalHeight, (int) (mKeyguardStatusView.getHeight() - mShelfHeight / 2.0f - - mDarkIconSize / 2.0f), clockPreferredY, hasCustomClock(), + totalHeight, + (int) (mKeyguardStatusViewController.getHeight() + - mShelfHeight / 2.0f - mDarkIconSize / 2.0f), + clockPreferredY, hasCustomClock(), hasVisibleNotifications, mInterpolatedDarkAmount, mEmptyDragAmount, bypassEnabled, getUnlockedStackScrollerPadding()); mClockPositionAlgorithm.run(mClockPositionResult); - PropertyAnimator.setProperty(mKeyguardStatusView, AnimatableProperty.X, - mClockPositionResult.clockX, CLOCK_ANIMATION_PROPERTIES, animateClock); - PropertyAnimator.setProperty(mKeyguardStatusView, AnimatableProperty.Y, - mClockPositionResult.clockY, CLOCK_ANIMATION_PROPERTIES, animateClock); + mKeyguardStatusViewController.updatePosition( + mClockPositionResult.clockX, mClockPositionResult.clockY, animateClock); updateNotificationTranslucency(); updateClock(); stackScrollerPadding = mClockPositionResult.stackScrollerPaddingExpanded; @@ -910,7 +911,7 @@ public class NotificationPanelViewController extends PanelViewController { float availableSpace = mNotificationStackScrollLayoutController.getHeight() - minPadding - shelfSize - Math.max(mIndicationBottomPadding, mAmbientIndicationBottomPadding) - - mKeyguardStatusView.getLogoutButtonHeight(); + - mKeyguardStatusViewController.getLogoutButtonHeight(); int count = 0; ExpandableView previousView = null; for (int i = 0; i < mNotificationStackScrollLayoutController.getChildCount(); i++) { @@ -1005,9 +1006,7 @@ public class NotificationPanelViewController extends PanelViewController { } private void updateClock() { - if (!mKeyguardStatusViewAnimating) { - mKeyguardStatusView.setAlpha(mClockPositionResult.clockAlpha); - } + mKeyguardStatusViewController.setAlpha(mClockPositionResult.clockAlpha); } public void animateToFullShade(long delay) { @@ -1605,29 +1604,6 @@ public class NotificationPanelViewController extends PanelViewController { } } - private final Runnable mAnimateKeyguardStatusViewInvisibleEndRunnable = new Runnable() { - @Override - public void run() { - mKeyguardStatusViewAnimating = false; - mKeyguardStatusView.setVisibility(View.INVISIBLE); - } - }; - - private final Runnable mAnimateKeyguardStatusViewGoneEndRunnable = new Runnable() { - @Override - public void run() { - mKeyguardStatusViewAnimating = false; - mKeyguardStatusView.setVisibility(View.GONE); - } - }; - - private final Runnable mAnimateKeyguardStatusViewVisibleEndRunnable = new Runnable() { - @Override - public void run() { - mKeyguardStatusViewAnimating = false; - } - }; - private final Runnable mAnimateKeyguardStatusBarInvisibleEndRunnable = new Runnable() { @Override public void run() { @@ -1705,46 +1681,6 @@ public class NotificationPanelViewController extends PanelViewController { } } - private void setKeyguardStatusViewVisibility(int statusBarState, boolean keyguardFadingAway, - boolean goingToFullShade) { - mKeyguardStatusView.animate().cancel(); - mKeyguardStatusViewAnimating = false; - if ((!keyguardFadingAway && mBarState == KEYGUARD - && statusBarState != KEYGUARD) || goingToFullShade) { - mKeyguardStatusViewAnimating = true; - mKeyguardStatusView.animate().alpha(0f).setStartDelay(0).setDuration( - 160).setInterpolator(Interpolators.ALPHA_OUT).withEndAction( - mAnimateKeyguardStatusViewGoneEndRunnable); - if (keyguardFadingAway) { - mKeyguardStatusView.animate().setStartDelay( - mKeyguardStateController.getKeyguardFadingAwayDelay()).setDuration( - mKeyguardStateController.getShortenedFadingAwayDuration()).start(); - } - } else if (mBarState == StatusBarState.SHADE_LOCKED - && statusBarState == KEYGUARD) { - mKeyguardStatusView.setVisibility(View.VISIBLE); - mKeyguardStatusViewAnimating = true; - mKeyguardStatusView.setAlpha(0f); - mKeyguardStatusView.animate().alpha(1f).setStartDelay(0).setDuration( - 320).setInterpolator(Interpolators.ALPHA_IN).withEndAction( - mAnimateKeyguardStatusViewVisibleEndRunnable); - } else if (statusBarState == KEYGUARD) { - if (keyguardFadingAway) { - mKeyguardStatusViewAnimating = true; - mKeyguardStatusView.animate().alpha(0).translationYBy( - -getHeight() * 0.05f).setInterpolator( - Interpolators.FAST_OUT_LINEAR_IN).setDuration(125).setStartDelay( - 0).withEndAction(mAnimateKeyguardStatusViewInvisibleEndRunnable).start(); - } else { - mKeyguardStatusView.setVisibility(View.VISIBLE); - mKeyguardStatusView.setAlpha(1f); - } - } else { - mKeyguardStatusView.setVisibility(View.GONE); - mKeyguardStatusView.setAlpha(1f); - } - } - private void updateQsState() { mNotificationStackScrollLayoutController.setQsExpanded(mQsExpanded); mNotificationStackScrollLayoutController.setScrollingEnabled( @@ -2075,7 +2011,7 @@ public class NotificationPanelViewController extends PanelViewController { private int getMaxPanelHeightBypass() { int position = mClockPositionAlgorithm.getExpandedClockPosition() - + mKeyguardStatusView.getHeight(); + + mKeyguardStatusViewController.getHeight(); if (mNotificationStackScrollLayoutController.getVisibleNotificationCount() != 0) { position += mShelfHeight / 2.0f + mDarkIconSize / 2.0f; } @@ -2156,7 +2092,7 @@ public class NotificationPanelViewController extends PanelViewController { int minKeyguardPanelBottom = mClockPositionAlgorithm.getExpandedClockPosition() - + mKeyguardStatusView.getHeight() + + mKeyguardStatusViewController.getHeight() + mNotificationStackScrollLayoutController.getIntrinsicContentHeight(); return Math.max(maxHeight, minKeyguardPanelBottom); } else { @@ -2604,7 +2540,7 @@ public class NotificationPanelViewController extends PanelViewController { } public void onScreenTurningOn() { - mKeyguardStatusView.dozeTimeTick(); + mKeyguardStatusViewController.dozeTimeTick(); } @Override @@ -2989,7 +2925,6 @@ public class NotificationPanelViewController extends PanelViewController { mAnimateNextPositionUpdate = false; } mNotificationStackScrollLayoutController.setPulsing(pulsing, animatePulse); - mKeyguardStatusView.setPulsing(pulsing); } public void setAmbientIndicationBottomPadding(int ambientIndicationBottomPadding) { @@ -3001,14 +2936,14 @@ public class NotificationPanelViewController extends PanelViewController { public void dozeTimeTick() { mKeyguardBottomArea.dozeTimeTick(); - mKeyguardStatusView.dozeTimeTick(); + mKeyguardStatusViewController.dozeTimeTick(); if (mInterpolatedDarkAmount > 0) { positionClockAndNotifications(); } } public void setStatusAccessibilityImportance(int mode) { - mKeyguardStatusView.setImportantForAccessibility(mode); + mKeyguardStatusViewController.setStatusAccessibilityImportance(mode); } /** @@ -3068,8 +3003,11 @@ public class NotificationPanelViewController extends PanelViewController { * security view of the bouncer. */ public void onBouncerPreHideAnimation() { - setKeyguardStatusViewVisibility(mBarState, true /* keyguardFadingAway */, - false /* goingToFullShade */); + mKeyguardStatusViewController.setKeyguardStatusViewVisibility( + mBarState, + true /* keyguardFadingAway */, + false /* goingToFullShade */, + mBarState); } /** @@ -3639,7 +3577,11 @@ public class NotificationPanelViewController extends PanelViewController { int oldState = mBarState; boolean keyguardShowing = statusBarState == KEYGUARD; - setKeyguardStatusViewVisibility(statusBarState, keyguardFadingAway, goingToFullShade); + mKeyguardStatusViewController.setKeyguardStatusViewVisibility( + statusBarState, + keyguardFadingAway, + goingToFullShade, + mBarState); setKeyguardBottomAreaVisibility(statusBarState, goingToFullShade); mBarState = statusBarState; @@ -3690,7 +3632,7 @@ public class NotificationPanelViewController extends PanelViewController { public void onDozeAmountChanged(float linearAmount, float amount) { mInterpolatedDarkAmount = amount; mLinearDarkAmount = linearAmount; - mKeyguardStatusView.setDarkAmount(mInterpolatedDarkAmount); + mKeyguardStatusViewController.setDarkAmount(mInterpolatedDarkAmount); mKeyguardBottomArea.setDarkAmount(mInterpolatedDarkAmount); positionClockAndNotifications(); } @@ -3736,9 +3678,10 @@ public class NotificationPanelViewController extends PanelViewController { setIsFullWidth(mNotificationStackScrollLayoutController.getWidth() == mView.getWidth()); // Update Clock Pivot - mKeyguardStatusView.setPivotX(mView.getWidth() / 2); - mKeyguardStatusView.setPivotY( - (FONT_HEIGHT - CAP_HEIGHT) / 2048f * mKeyguardStatusView.getClockTextSize()); + mKeyguardStatusViewController.setPivotX(mView.getWidth() / 2); + mKeyguardStatusViewController.setPivotY( + (FONT_HEIGHT - CAP_HEIGHT) / 2048f + * mKeyguardStatusViewController.getClockTextSize()); // Calculate quick setting heights. int oldMaxHeight = mQsMaxExpansionHeight; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java index e42c3dc4f589..6ed092f33d95 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java @@ -671,14 +671,18 @@ public class PhoneStatusBarPolicy mIconController.setIconVisibility(mSlotCamera, showCamera); mIconController.setIconVisibility(mSlotMicrophone, showMicrophone); - if (mPrivacyItemController.getAllIndicatorsAvailable()) { + if (mPrivacyItemController.getAllIndicatorsAvailable() + || mPrivacyItemController.getLocationAvailable()) { mIconController.setIconVisibility(mSlotLocation, showLocation); } } @Override public void onLocationActiveChanged(boolean active) { - if (!mPrivacyItemController.getAllIndicatorsAvailable()) updateLocationFromController(); + if (!mPrivacyItemController.getAllIndicatorsAvailable() + && !mPrivacyItemController.getLocationAvailable()) { + updateLocationFromController(); + } } // Updates the status view based on the current state of location requests. diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java index 456e99ceecb2..c7932bb44cf4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -69,6 +69,7 @@ import android.content.IntentFilter; import android.content.pm.IPackageManager; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; +import android.content.pm.ResolveInfo; import android.content.res.Configuration; import android.graphics.Point; import android.graphics.PointF; @@ -152,6 +153,7 @@ import com.android.systemui.dagger.qualifiers.UiBackground; import com.android.systemui.demomode.DemoMode; import com.android.systemui.demomode.DemoModeCommandReceiver; import com.android.systemui.demomode.DemoModeController; +import com.android.systemui.emergency.EmergencyGesture; import com.android.systemui.fragments.ExtensionFragmentListener; import com.android.systemui.fragments.FragmentHostManager; import com.android.systemui.keyguard.DismissCallbackRegistry; @@ -170,7 +172,7 @@ import com.android.systemui.plugins.qs.QS; import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper.SnoozeOption; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.qs.QSFragment; -import com.android.systemui.qs.QSPanel; +import com.android.systemui.qs.QSPanelController; import com.android.systemui.recents.ScreenPinningRequest; import com.android.systemui.shared.plugins.PluginManager; import com.android.systemui.shared.system.WindowManagerWrapper; @@ -406,7 +408,7 @@ public class StatusBar extends SystemUI implements DemoMode, protected NotificationPanelViewController mNotificationPanelViewController; // settings - private QSPanel mQSPanel; + private QSPanelController mQSPanelController; KeyguardIndicationController mKeyguardIndicationController; @@ -819,7 +821,7 @@ public class StatusBar extends SystemUI implements DemoMode, updateScrimController(); }; - + mActivityIntentHelper = new ActivityIntentHelper(mContext); DateTimeView.setReceiverHandler(timeTickHandler); } @@ -833,8 +835,6 @@ public class StatusBar extends SystemUI implements DemoMode, mBubblesOptional.get().setExpandListener(mBubbleExpandListener); } - mActivityIntentHelper = new ActivityIntentHelper(mContext); - mColorExtractor.addOnColorsChangedListener(this); mStatusBarStateController.addCallback(this, SysuiStatusBarStateController.RANK_STATUS_BAR); @@ -1200,8 +1200,8 @@ public class StatusBar extends SystemUI implements DemoMode, fragmentHostManager.addTagListener(QS.TAG, (tag, f) -> { QS qs = (QS) f; if (qs instanceof QSFragment) { - mQSPanel = ((QSFragment) qs).getQsPanel(); - mQSPanel.setBrightnessMirror(mBrightnessMirrorController); + mQSPanelController = ((QSFragment) qs).getQSPanelController(); + mQSPanelController.setBrightnessMirror(mBrightnessMirrorController); } }); } @@ -1593,19 +1593,19 @@ public class StatusBar extends SystemUI implements DemoMode, } public void addQsTile(ComponentName tile) { - if (mQSPanel != null && mQSPanel.getHost() != null) { - mQSPanel.getHost().addTile(tile); + if (mQSPanelController != null && mQSPanelController.getHost() != null) { + mQSPanelController.getHost().addTile(tile); } } public void remQsTile(ComponentName tile) { - if (mQSPanel != null && mQSPanel.getHost() != null) { - mQSPanel.getHost().removeTile(tile); + if (mQSPanelController != null && mQSPanelController.getHost() != null) { + mQSPanelController.getHost().removeTile(tile); } } public void clickTile(ComponentName tile) { - mQSPanel.clickTile(tile); + mQSPanelController.clickTile(tile); } /** @@ -2197,7 +2197,7 @@ public class StatusBar extends SystemUI implements DemoMode, if (!mUserSetup) return; if (subPanel != null) { - mQSPanel.openDetails(subPanel); + mQSPanelController.openDetails(subPanel); } mNotificationPanelViewController.expandWithQs(); @@ -2845,7 +2845,7 @@ public class StatusBar extends SystemUI implements DemoMode, resetUserExpandedStates(); } else if (DevicePolicyManager.ACTION_SHOW_DEVICE_MONITORING_DIALOG.equals(action)) { - mQSPanel.showDeviceMonitoringDialog(); + mQSPanelController.showDeviceMonitoringDialog(); } Trace.endSection(); } @@ -2936,8 +2936,8 @@ public class StatusBar extends SystemUI implements DemoMode, */ void updateResources() { // Update the quick setting tiles - if (mQSPanel != null) { - mQSPanel.updateResources(); + if (mQSPanelController != null) { + mQSPanelController.updateResources(); } if (mStatusBarWindowController != null) { @@ -3402,8 +3402,8 @@ public class StatusBar extends SystemUI implements DemoMode, // Keyguard state has changed, but QS is not listening anymore. Make sure to update the tile // visibilities so next time we open the panel we know the correct height already. - if (mQSPanel != null) { - mQSPanel.refreshAllTiles(); + if (mQSPanelController != null) { + mQSPanelController.refreshAllTiles(); } mHandler.removeMessages(MSG_LAUNCH_TRANSITION_TIMEOUT); releaseGestureWakeLock(); @@ -3980,6 +3980,28 @@ public class StatusBar extends SystemUI implements DemoMode, } } + @Override + public void onEmergencyActionLaunchGestureDetected() { + // TODO (b/169793384) Polish the panic gesture to be just like its older brother, camera. + Intent emergencyIntent = new Intent(EmergencyGesture.ACTION_LAUNCH_EMERGENCY); + PackageManager pm = mContext.getPackageManager(); + ResolveInfo resolveInfo = pm.resolveActivity(emergencyIntent, /*flags=*/0); + if (resolveInfo == null) { + // TODO(b/171084088) Upgrade log to wtf when we have default app in main branch. + Log.d(TAG, "Couldn't find an app to process the emergency intent."); + return; + } + + if (mVibrator != null && mVibrator.hasVibrator()) { + mVibrator.vibrate(500L); + } + + emergencyIntent.setComponent(new ComponentName(resolveInfo.activityInfo.packageName, + resolveInfo.activityInfo.name)); + emergencyIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + startActivity(emergencyIntent, /*dismissShade=*/true); + } + boolean isCameraAllowedByAdmin() { if (mDevicePolicyManager.getCameraDisabled(null, mLockscreenUserManager.getCurrentUserId())) { @@ -4282,6 +4304,19 @@ public class StatusBar extends SystemUI implements DemoMode, } public static Bundle getActivityOptions(@Nullable RemoteAnimationAdapter animationAdapter) { + return getDefaultActivityOptions(animationAdapter).toBundle(); + } + + public static Bundle getActivityOptions(@Nullable RemoteAnimationAdapter animationAdapter, + boolean isKeyguardShowing, long eventTime) { + ActivityOptions options = getDefaultActivityOptions(animationAdapter); + options.setSourceInfo(isKeyguardShowing ? ActivityOptions.SourceInfo.TYPE_LOCKSCREEN + : ActivityOptions.SourceInfo.TYPE_NOTIFICATION, eventTime); + return options.toBundle(); + } + + public static ActivityOptions getDefaultActivityOptions( + @Nullable RemoteAnimationAdapter animationAdapter) { ActivityOptions options; if (animationAdapter != null) { options = ActivityOptions.makeRemoteAnimation(animationAdapter); @@ -4291,7 +4326,7 @@ public class StatusBar extends SystemUI implements DemoMode, // Anything launched from the notification shade should always go into the secondary // split-screen windowing mode. options.setLaunchWindowingMode(WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY); - return options.toBundle(); + return options; } void visibilityChanged(boolean visible) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java index aa01642e5c17..256ee2081f41 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java @@ -30,6 +30,7 @@ import android.app.TaskStackBuilder; import android.content.Context; import android.content.Intent; import android.os.AsyncTask; +import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.os.RemoteException; @@ -40,7 +41,6 @@ import android.service.notification.StatusBarNotification; import android.text.TextUtils; import android.util.EventLog; import android.view.RemoteAnimationAdapter; -import android.view.View; import com.android.internal.logging.MetricsLogger; import com.android.internal.statusbar.NotificationVisibility; @@ -402,7 +402,7 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit PendingIntent intent, Intent fillInIntent, NotificationEntry entry, - View row, + ExpandableNotificationRow row, boolean wasOccluded, boolean isActivityIntent) { RemoteAnimationAdapter adapter = mActivityLaunchAnimator.getLaunchAnimation(row, @@ -414,8 +414,11 @@ public class StatusBarNotificationActivityStarter implements NotificationActivit .registerRemoteAnimationForNextActivityStart( intent.getCreatorPackage(), adapter); } + long eventTime = row.getAndResetLastActionUpTime(); + Bundle options = eventTime > 0 ? getActivityOptions(adapter, + mKeyguardStateController.isShowing(), eventTime) : getActivityOptions(adapter); int launchResult = intent.sendAndReturnResult(mContext, 0, fillInIntent, null, - null, null, getActivityOptions(adapter)); + null, null, options); mMainThreadHandler.post(() -> { mActivityLaunchAnimator.setLaunchResult(launchResult, isActivityIntent); }); 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 9c3395f9332d..c84589a9e142 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java @@ -18,11 +18,13 @@ package com.android.systemui.statusbar.policy; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; +import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityManager; import android.app.Notification; import android.app.PendingIntent; import android.app.RemoteInput; +import android.content.ClipData; import android.content.ClipDescription; import android.content.Context; import android.content.Intent; @@ -43,6 +45,7 @@ import android.util.Log; import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.MotionEvent; +import android.view.OnReceiveContentCallback; import android.view.View; import android.view.ViewAnimationUtils; import android.view.ViewGroup; @@ -57,9 +60,6 @@ import android.widget.LinearLayout; import android.widget.ProgressBar; import android.widget.TextView; -import androidx.core.view.inputmethod.InputConnectionCompat; -import androidx.core.view.inputmethod.InputContentInfoCompat; - import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto; import com.android.internal.statusbar.IStatusBarService; @@ -73,7 +73,9 @@ import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewW import com.android.systemui.statusbar.notification.stack.StackStateAnimator; import com.android.systemui.statusbar.phone.LightBarController; +import java.util.Collections; import java.util.HashMap; +import java.util.Set; import java.util.function.Consumer; /** @@ -313,6 +315,7 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene mRemoteInputs = remoteInputs; mRemoteInput = remoteInput; mEditText.setHint(mRemoteInput.getLabel()); + mEditText.mSupportedMimeTypes = remoteInput.getAllowedDataTypes(); mEntry.editedSuggestionInfo = editedSuggestionInfo; if (editedSuggestionInfo != null) { @@ -571,6 +574,7 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene boolean mShowImeOnInputConnection; private LightBarController mLightBarController; UserHandle mUser; + private Set<String> mSupportedMimeTypes; public RemoteEditText(Context context, AttributeSet attrs) { super(context, attrs); @@ -578,6 +582,40 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene mLightBarController = Dependency.get(LightBarController.class); } + @Override + protected void onFinishInflate() { + super.onFinishInflate(); + setOnReceiveContentCallback(new OnReceiveContentCallback<View>() { + @Override + public boolean onReceiveContent(@NonNull View view, @NonNull Payload payload) { + ClipData clip = payload.getClip(); + if (clip.getItemCount() == 0) { + return false; + } + Uri contentUri = clip.getItemAt(0).getUri(); + ClipDescription description = clip.getDescription(); + String mimeType = null; + if (description.getMimeTypeCount() > 0) { + mimeType = description.getMimeType(0); + } + if (mimeType != null) { + Intent dataIntent = mRemoteInputView + .prepareRemoteInputFromData(mimeType, contentUri); + mRemoteInputView.sendRemoteInput(dataIntent); + } + return true; + } + + @NonNull + @Override + public Set<String> getSupportedMimeTypes(@NonNull View view) { + return mSupportedMimeTypes != null + ? mSupportedMimeTypes + : Collections.emptySet(); + } + }); + } + private void defocusIfNeeded(boolean animate) { if (mRemoteInputView != null && mRemoteInputView.mEntry.getRow().isChangingPosition() || isTemporarilyDetached()) { @@ -670,36 +708,7 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene @Override public InputConnection onCreateInputConnection(EditorInfo outAttrs) { - // TODO: Pass RemoteInput data types to allow image insertion. - // String[] allowedDataTypes = mRemoteInputView.mRemoteInput.getAllowedDataTypes() - // .toArray(new String[0]); - // EditorInfoCompat.setContentMimeTypes(outAttrs, allowedDataTypes); - final InputConnection inputConnection = super.onCreateInputConnection(outAttrs); - - final InputConnectionCompat.OnCommitContentListener callback = - new InputConnectionCompat.OnCommitContentListener() { - @Override - public boolean onCommitContent( - InputContentInfoCompat inputContentInfoCompat, int i, - Bundle bundle) { - Uri contentUri = inputContentInfoCompat.getContentUri(); - ClipDescription description = inputContentInfoCompat.getDescription(); - String mimeType = null; - if (description != null && description.getMimeTypeCount() > 0) { - mimeType = description.getMimeType(0); - } - if (mimeType != null) { - Intent dataIntent = mRemoteInputView.prepareRemoteInputFromData( - mimeType, contentUri); - mRemoteInputView.sendRemoteInput(dataIntent); - } - return true; - } - }; - - InputConnection ic = inputConnection == null ? null : - InputConnectionCompat.createWrapper(inputConnection, outAttrs, callback); - + final InputConnection ic = super.onCreateInputConnection(outAttrs); Context userContext = null; try { userContext = mContext.createPackageContextAsUser( @@ -747,7 +756,6 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene } else { setBackground(null); } - } } } diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java index 70bba263ab90..b67574d1c4de 100644 --- a/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java +++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java @@ -32,6 +32,7 @@ import com.android.systemui.statusbar.phone.SystemUIDialog; public abstract class TunerService { public static final String ACTION_CLEAR = "com.android.systemui.action.CLEAR_TUNER"; + private final Context mContext; public abstract void clearAll(); public abstract void destroy(); @@ -50,6 +51,10 @@ public abstract class TunerService { void onTuningChanged(String key, String newValue); } + public TunerService(Context context) { + mContext = context; + } + private static Context userContext(Context context, UserHandle user) { try { return context.createPackageContextAsUser(context.getPackageName(), 0, user); @@ -58,6 +63,11 @@ public abstract class TunerService { } } + /** Enables or disables the tuner for the supplied user. */ + public void setTunerEnabled(UserHandle user, boolean enabled) { + setTunerEnabled(mContext, user, enabled); + } + public static final void setTunerEnabled(Context context, UserHandle user, boolean enabled) { userContext(context, user).getPackageManager().setComponentEnabledSetting( new ComponentName(context, TunerActivity.class), @@ -66,6 +76,11 @@ public abstract class TunerService { PackageManager.DONT_KILL_APP); } + /** Returns true if the tuner is enabled for the supplied user. */ + public boolean isTunerEnabled(UserHandle user) { + return isTunerEnabled(mContext, user); + } + public static final boolean isTunerEnabled(Context context, UserHandle user) { return userContext(context, user).getPackageManager().getComponentEnabledSetting( new ComponentName(context, TunerActivity.class)) @@ -81,6 +96,11 @@ public abstract class TunerService { } } + /** */ + public void showResetRequest(UserHandle user, final Runnable onDisabled) { + showResetRequest(mContext, user, onDisabled); + } + public static final void showResetRequest(final Context context, UserHandle user, final Runnable onDisabled) { SystemUIDialog dialog = new SystemUIDialog(context); diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java index 22f03e074b06..027c282ba352 100644 --- a/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java +++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java @@ -94,6 +94,7 @@ public class TunerServiceImpl extends TunerService { DemoModeController demoModeController, BroadcastDispatcher broadcastDispatcher, UserTracker userTracker) { + super(context); mContext = context; mContentResolver = mContext.getContentResolver(); mLeakDetector = leakDetector; diff --git a/packages/SystemUI/src/com/android/systemui/tv/TvGlobalRootComponent.java b/packages/SystemUI/src/com/android/systemui/tv/TvGlobalRootComponent.java index df741a0e98ff..89ab23b50cd8 100644 --- a/packages/SystemUI/src/com/android/systemui/tv/TvGlobalRootComponent.java +++ b/packages/SystemUI/src/com/android/systemui/tv/TvGlobalRootComponent.java @@ -42,6 +42,12 @@ public interface TvGlobalRootComponent extends GlobalRootComponent { TvGlobalRootComponent build(); } + /** + * Builder for a WMComponent. + */ + @Override + TvWMComponent.Builder getWMComponentBuilder(); + @Override TvSysUIComponent.Builder getSysUIComponent(); } diff --git a/packages/SystemUI/src/com/android/systemui/tv/TvSysUIComponentModule.java b/packages/SystemUI/src/com/android/systemui/tv/TvSysUIComponentModule.java index 334bb013ae69..9621e5f5f1a0 100644 --- a/packages/SystemUI/src/com/android/systemui/tv/TvSysUIComponentModule.java +++ b/packages/SystemUI/src/com/android/systemui/tv/TvSysUIComponentModule.java @@ -19,7 +19,7 @@ package com.android.systemui.tv; import dagger.Module; /** - * Dagger module for including the WMComponent. + * Dagger module for including the SysUIComponent. */ @Module(subcomponents = {TvSysUIComponent.class}) public abstract class TvSysUIComponentModule { diff --git a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIBinder.java b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIBinder.java index bde88b1b5533..2c3ea4f452bb 100644 --- a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIBinder.java +++ b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIBinder.java @@ -22,7 +22,7 @@ import com.android.systemui.wmshell.TvPipModule; import dagger.Binds; import dagger.Module; -@Module(includes = TvPipModule.class) +@Module interface TvSystemUIBinder { @Binds GlobalRootComponent bindGlobalRootComponent(TvGlobalRootComponent globalRootComponent); diff --git a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java index c5bb9c1b6f48..8ffc7cf568ff 100644 --- a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java +++ b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java @@ -62,7 +62,6 @@ import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.DeviceProvisionedController; import com.android.systemui.statusbar.policy.DeviceProvisionedControllerImpl; import com.android.systemui.statusbar.policy.HeadsUpManager; -import com.android.systemui.wmshell.TvWMShellModule; import javax.inject.Named; @@ -75,8 +74,7 @@ import dagger.Provides; * overridden by the System UI implementation. */ @Module(includes = { - QSModule.class, - TvWMShellModule.class, + QSModule.class }, subcomponents = { }) diff --git a/packages/SystemUI/src/com/android/systemui/tv/TvWMComponent.java b/packages/SystemUI/src/com/android/systemui/tv/TvWMComponent.java new file mode 100644 index 000000000000..f678513f2ce6 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/tv/TvWMComponent.java @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.tv; + +import com.android.systemui.dagger.WMComponent; +import com.android.systemui.dagger.WMSingleton; +import com.android.systemui.wmshell.TvWMShellModule; + +import dagger.Subcomponent; + + +/** + * Dagger Subcomponent for WindowManager. + */ +@WMSingleton +@Subcomponent(modules = {TvWMShellModule.class}) +public interface TvWMComponent extends WMComponent { + + /** + * Builder for a SysUIComponent. + */ + @Subcomponent.Builder + interface Builder extends WMComponent.Builder { + TvWMComponent build(); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/util/DeviceConfigProxy.java b/packages/SystemUI/src/com/android/systemui/util/DeviceConfigProxy.java index 66f8f74c7cab..6b5556b3ea91 100644 --- a/packages/SystemUI/src/com/android/systemui/util/DeviceConfigProxy.java +++ b/packages/SystemUI/src/com/android/systemui/util/DeviceConfigProxy.java @@ -23,13 +23,19 @@ import android.content.Context; import android.provider.DeviceConfig; import android.provider.Settings; +import com.android.systemui.dagger.SysUISingleton; + import java.util.concurrent.Executor; +import javax.inject.Inject; + /** * Wrapper around DeviceConfig useful for testing. */ +@SysUISingleton public class DeviceConfigProxy { + @Inject public DeviceConfigProxy() { } diff --git a/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java b/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java index 344f0d2f5506..e79d432b3b15 100644 --- a/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java +++ b/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java @@ -24,7 +24,6 @@ import android.view.LayoutInflater; import android.view.View; import com.android.systemui.dagger.SysUISingleton; -import com.android.systemui.qs.QSFooterImpl; import com.android.systemui.qs.QSPanel; import com.android.systemui.qs.QuickQSPanel; import com.android.systemui.qs.customize.QSCustomizer; @@ -92,11 +91,6 @@ public class InjectionInflationController { } /** - * Creates the QSFooterImpl. - */ - QSFooterImpl createQsFooter(); - - /** * Creates the NotificationStackScrollLayout. */ NotificationStackScrollLayout createNotificationStackScrollLayout(); diff --git a/packages/SystemUI/src/com/android/systemui/util/ViewController.java b/packages/SystemUI/src/com/android/systemui/util/ViewController.java index c7aa780fcacb..3dbc6f101a90 100644 --- a/packages/SystemUI/src/com/android/systemui/util/ViewController.java +++ b/packages/SystemUI/src/com/android/systemui/util/ViewController.java @@ -16,6 +16,8 @@ package com.android.systemui.util; +import android.content.Context; +import android.content.res.Resources; import android.view.View; import android.view.View.OnAttachStateChangeListener; @@ -75,6 +77,14 @@ public abstract class ViewController<T extends View> { } } + protected Context getContext() { + return mView.getContext(); + } + + protected Resources getResources() { + return mView.getResources(); + } + /** * Called when the view is attached and a call to {@link #init()} has been made in either order. */ diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java index f55445ca1de3..5310b3f2c8bb 100644 --- a/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java +++ b/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java @@ -20,7 +20,7 @@ import android.content.Context; import android.os.Handler; import android.view.LayoutInflater; -import com.android.systemui.dagger.SysUISingleton; +import com.android.systemui.dagger.WMSingleton; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.WindowManagerShellWrapper; import com.android.wm.shell.common.DisplayController; @@ -46,52 +46,57 @@ import dagger.Provides; */ @Module public abstract class TvPipModule { - - @SysUISingleton + @WMSingleton @Provides - static Pip providePipController(Context context, + static Optional<Pip> providePip( + Context context, PipBoundsHandler pipBoundsHandler, PipTaskOrganizer pipTaskOrganizer, WindowManagerShellWrapper windowManagerShellWrapper) { - return new PipController(context, pipBoundsHandler, pipTaskOrganizer, - windowManagerShellWrapper); + return Optional.of( + new PipController( + context, + pipBoundsHandler, + pipTaskOrganizer, + windowManagerShellWrapper)); } - @SysUISingleton + @WMSingleton @Provides - static PipControlsViewController providePipControlsViewContrller( + static PipControlsViewController providePipControlsViewController( PipControlsView pipControlsView, PipController pipController, LayoutInflater layoutInflater, Handler handler) { return new PipControlsViewController(pipControlsView, pipController, layoutInflater, handler); } - @SysUISingleton + @WMSingleton @Provides static PipControlsView providePipControlsView(Context context) { return new PipControlsView(context, null); } - @SysUISingleton + @WMSingleton @Provides static PipNotification providePipNotification(Context context, PipController pipController) { return new PipNotification(context, pipController); } - @SysUISingleton + @WMSingleton @Provides - static PipBoundsHandler providePipBoundsHandler(Context context) { - return new PipBoundsHandler(context); + static PipBoundsHandler providePipBoundsHandler(Context context, + PipBoundsState pipBoundsState) { + return new PipBoundsHandler(context, pipBoundsState); } - @SysUISingleton + @WMSingleton @Provides static PipBoundsState providePipBoundsState() { return new PipBoundsState(); } - @SysUISingleton + @WMSingleton @Provides static PipTaskOrganizer providePipTaskOrganizer(Context context, PipBoundsState pipBoundsState, diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/TvWMShellModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/TvWMShellModule.java index 56efffc29d85..294c749a2abe 100644 --- a/packages/SystemUI/src/com/android/systemui/wmshell/TvWMShellModule.java +++ b/packages/SystemUI/src/com/android/systemui/wmshell/TvWMShellModule.java @@ -20,7 +20,7 @@ import android.content.Context; import android.os.Handler; import android.view.IWindowManager; -import com.android.systemui.dagger.SysUISingleton; +import com.android.systemui.dagger.WMSingleton; import com.android.systemui.dagger.qualifiers.Main; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.common.DisplayController; @@ -40,10 +40,9 @@ import dagger.Provides; * Provides dependencies from {@link com.android.wm.shell} which could be customized among different * branches of SystemUI. */ -// TODO(b/162923491): Move most of these dependencies into WMSingleton scope. @Module(includes = {WMShellBaseModule.class, TvPipModule.class}) public class TvWMShellModule { - @SysUISingleton + @WMSingleton @Provides static DisplayImeController provideDisplayImeController(IWindowManager wmService, DisplayController displayController, @Main Executor mainExecutor, @@ -52,6 +51,8 @@ public class TvWMShellModule { transactionPool); } + @WMSingleton + @Provides static SplitScreen provideSplitScreen(Context context, DisplayController displayController, SystemWindows systemWindows, DisplayImeController displayImeController, @Main Handler handler, diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java index 9281a090fd97..89ea9e24ea51 100644 --- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java +++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java @@ -65,7 +65,6 @@ import com.android.systemui.statusbar.policy.UserInfoController; import com.android.systemui.tracing.ProtoTracer; import com.android.systemui.tracing.nano.SystemUiTraceProto; import com.android.wm.shell.ShellTaskOrganizer; -import com.android.wm.shell.common.DisplayImeController; import com.android.wm.shell.nano.WmShellTraceProto; import com.android.wm.shell.onehanded.OneHanded; import com.android.wm.shell.onehanded.OneHandedEvents; @@ -101,19 +100,18 @@ public final class WMShell extends SystemUI private final CommandQueue mCommandQueue; private final ConfigurationController mConfigurationController; - private final DisplayImeController mDisplayImeController; private final InputConsumerController mInputConsumerController; private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; private final TaskStackChangeListeners mTaskStackChangeListeners; private final NavigationModeController mNavigationModeController; private final ScreenLifecycle mScreenLifecycle; private final SysUiState mSysUiState; + // TODO: This is only here because we need to dump state. Remove and replace with a dumper + // interface. + private final ShellTaskOrganizer mShellTaskOrganizer; private final Optional<Pip> mPipOptional; private final Optional<SplitScreen> mSplitScreenOptional; private final Optional<OneHanded> mOneHandedOptional; - // Inject the organizer directly in case the optionals aren't loaded to depend on it. There - // are non-optional windowing features like FULLSCREEN. - private final ShellTaskOrganizer mShellTaskOrganizer; private final ProtoTracer mProtoTracer; private boolean mIsSysUiStateValid; private KeyguardUpdateMonitorCallback mSplitScreenKeyguardCallback; @@ -126,7 +124,6 @@ public final class WMShell extends SystemUI InputConsumerController inputConsumerController, KeyguardUpdateMonitor keyguardUpdateMonitor, TaskStackChangeListeners taskStackChangeListeners, - DisplayImeController displayImeController, NavigationModeController navigationModeController, ScreenLifecycle screenLifecycle, SysUiState sysUiState, @@ -141,7 +138,6 @@ public final class WMShell extends SystemUI mInputConsumerController = inputConsumerController; mKeyguardUpdateMonitor = keyguardUpdateMonitor; mTaskStackChangeListeners = taskStackChangeListeners; - mDisplayImeController = displayImeController; mNavigationModeController = navigationModeController; mScreenLifecycle = screenLifecycle; mSysUiState = sysUiState; @@ -156,10 +152,6 @@ public final class WMShell extends SystemUI @Override public void start() { mCommandQueue.addCallback(this); - // This is to prevent circular init problem by separating registration step out of its - // constructor. And make sure the initialization of DisplayImeController won't depend on - // specific feature anymore. - mDisplayImeController.startMonitorDisplays(); mPipOptional.ifPresent(this::initPip); mSplitScreenOptional.ifPresent(this::initSplitScreen); mOneHandedOptional.ifPresent(this::initOneHanded); @@ -167,9 +159,6 @@ public final class WMShell extends SystemUI @VisibleForTesting void initPip(Pip pip) { - if (!PipUtils.hasSystemFeature(mContext)) { - return; - } mCommandQueue.addCallback(new CommandQueue.Callbacks() { @Override public void showPictureInPictureMenu() { @@ -204,6 +193,7 @@ public final class WMShell extends SystemUI } }); + // TODO: Move this into the shell // Handle for system task stack changes. mTaskStackChangeListeners.registerTaskStackListener( new TaskStackChangeListener() { @@ -420,6 +410,11 @@ public final class WMShell extends SystemUI return; } // Dump WMShell stuff here if no commands were handled + mShellTaskOrganizer.dump(pw, ""); + pw.println(); + pw.println(); + mPipOptional.ifPresent(pip -> pip.dump(pw)); + mSplitScreenOptional.ifPresent(splitScreen -> splitScreen.dump(pw)); mOneHandedOptional.ifPresent(oneHanded -> oneHanded.dump(pw)); } diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java index 09678b5d1772..b333f8b36226 100644 --- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java +++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java @@ -20,28 +20,22 @@ import android.app.IActivityManager; import android.content.Context; import android.content.pm.PackageManager; import android.os.Handler; -import android.util.DisplayMetrics; import android.view.IWindowManager; import com.android.internal.logging.UiEventLogger; import com.android.systemui.bubbles.Bubbles; -import com.android.systemui.dagger.SysUISingleton; +import com.android.systemui.dagger.WMSingleton; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.shared.system.InputConsumerController; -import com.android.systemui.util.DeviceConfigProxy; import com.android.wm.shell.ShellTaskOrganizer; -import com.android.wm.shell.WindowManagerShellWrapper; -import com.android.wm.shell.animation.FlingAnimationUtils; import com.android.wm.shell.common.AnimationThread; import com.android.wm.shell.common.DisplayController; -import com.android.wm.shell.common.FloatingContentCoordinator; import com.android.wm.shell.common.HandlerExecutor; import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.common.SystemWindows; import com.android.wm.shell.common.TransactionPool; import com.android.wm.shell.onehanded.OneHanded; import com.android.wm.shell.onehanded.OneHandedController; -import com.android.wm.shell.pip.Pip; import com.android.wm.shell.pip.PipSurfaceTransactionHelper; import com.android.wm.shell.pip.PipUiEventLogger; import com.android.wm.shell.pip.phone.PipAppOpsListener; @@ -59,41 +53,28 @@ import dagger.Provides; * Provides basic dependencies from {@link com.android.wm.shell}, the dependencies declared here * should be shared among different branches of SystemUI. */ -// TODO(b/162923491): Move most of these dependencies into WMSingleton scope. @Module public abstract class WMShellBaseModule { - @SysUISingleton + @WMSingleton @Provides static TransactionPool provideTransactionPool() { return new TransactionPool(); } - @SysUISingleton + @WMSingleton @Provides static DisplayController provideDisplayController(Context context, @Main Handler handler, IWindowManager wmService) { return new DisplayController(context, handler, wmService); } - @SysUISingleton - @Provides - static DeviceConfigProxy provideDeviceConfigProxy() { - return new DeviceConfigProxy(); - } - - @SysUISingleton + @WMSingleton @Provides static InputConsumerController provideInputConsumerController() { return InputConsumerController.getPipInputConsumer(); } - @SysUISingleton - @Provides - static FloatingContentCoordinator provideFloatingContentCoordinator() { - return new FloatingContentCoordinator(); - } - - @SysUISingleton + @WMSingleton @Provides static PipAppOpsListener providePipAppOpsListener(Context context, IActivityManager activityManager, @@ -101,73 +82,55 @@ public abstract class WMShellBaseModule { return new PipAppOpsListener(context, activityManager, pipTouchHandler.getMotionHelper()); } - @SysUISingleton + @WMSingleton @Provides static PipMediaController providePipMediaController(Context context, IActivityManager activityManager) { return new PipMediaController(context, activityManager); } - @SysUISingleton + @WMSingleton @Provides static PipUiEventLogger providePipUiEventLogger(UiEventLogger uiEventLogger, PackageManager packageManager) { return new PipUiEventLogger(uiEventLogger, packageManager); } - @SysUISingleton + @WMSingleton @Provides - static PipSurfaceTransactionHelper providesPipSurfaceTransactionHelper(Context context) { + static PipSurfaceTransactionHelper providePipSurfaceTransactionHelper(Context context) { return new PipSurfaceTransactionHelper(context); } - @SysUISingleton + @WMSingleton @Provides static SystemWindows provideSystemWindows(DisplayController displayController, IWindowManager wmService) { return new SystemWindows(displayController, wmService); } - @SysUISingleton + @WMSingleton @Provides static SyncTransactionQueue provideSyncTransactionQueue(@Main Handler handler, TransactionPool pool) { return new SyncTransactionQueue(pool, handler); } - @SysUISingleton + @WMSingleton @Provides static ShellTaskOrganizer provideShellTaskOrganizer(SyncTransactionQueue syncQueue, @Main Handler handler, TransactionPool transactionPool) { - ShellTaskOrganizer organizer = new ShellTaskOrganizer(syncQueue, transactionPool, + return new ShellTaskOrganizer(syncQueue, transactionPool, new HandlerExecutor(handler), AnimationThread.instance().getExecutor()); - organizer.registerOrganizer(); - return organizer; - } - - @SysUISingleton - @Provides - static WindowManagerShellWrapper provideWindowManagerShellWrapper() { - return new WindowManagerShellWrapper(); } - @SysUISingleton - @Provides - static FlingAnimationUtils.Builder provideFlingAnimationUtilsBuilder( - DisplayMetrics displayMetrics) { - return new FlingAnimationUtils.Builder(displayMetrics); - } - - @BindsOptionalOf - abstract Pip optionalPip(); - @BindsOptionalOf abstract SplitScreen optionalSplitScreen(); @BindsOptionalOf abstract Bubbles optionalBubbles(); - @SysUISingleton + @WMSingleton @Provides static Optional<OneHanded> provideOneHandedController(Context context, DisplayController displayController) { diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java index 975757a4c259..a6fe72850716 100644 --- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java +++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java @@ -20,7 +20,7 @@ import android.content.Context; import android.os.Handler; import android.view.IWindowManager; -import com.android.systemui.dagger.SysUISingleton; +import com.android.systemui.dagger.WMSingleton; import com.android.systemui.dagger.qualifiers.Main; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.WindowManagerShellWrapper; @@ -54,10 +54,9 @@ import dagger.Provides; * Provides dependencies from {@link com.android.wm.shell} which could be customized among different * branches of SystemUI. */ -// TODO(b/162923491): Move most of these dependencies into WMSingleton scope. @Module(includes = WMShellBaseModule.class) public class WMShellModule { - @SysUISingleton + @WMSingleton @Provides static DisplayImeController provideDisplayImeController(IWindowManager wmService, DisplayController displayController, @Main Executor mainExecutor, @@ -66,25 +65,7 @@ public class WMShellModule { transactionPool); } - @SysUISingleton - @Provides - static Pip providePipController(Context context, - DisplayController displayController, - PipAppOpsListener pipAppOpsListener, - PipBoundsHandler pipBoundsHandler, - PipBoundsState pipBoundsState, - PipMediaController pipMediaController, - PipMenuActivityController pipMenuActivityController, - PipTaskOrganizer pipTaskOrganizer, - PipTouchHandler pipTouchHandler, - WindowManagerShellWrapper windowManagerShellWrapper) { - return new PipController(context, displayController, - pipAppOpsListener, pipBoundsHandler, pipBoundsState, pipMediaController, - pipMenuActivityController, pipTaskOrganizer, pipTouchHandler, - windowManagerShellWrapper); - } - - @SysUISingleton + @WMSingleton @Provides static SplitScreen provideSplitScreen(Context context, DisplayController displayController, SystemWindows systemWindows, @@ -95,28 +76,42 @@ public class WMShellModule { displayImeController, handler, transactionPool, shellTaskOrganizer, syncQueue); } - @SysUISingleton + @WMSingleton + @Provides + static Optional<Pip> providePip(Context context, DisplayController displayController, + PipAppOpsListener pipAppOpsListener, PipBoundsHandler pipBoundsHandler, + PipBoundsState pipBoundsState, PipMediaController pipMediaController, + PipMenuActivityController pipMenuActivityController, PipTaskOrganizer pipTaskOrganizer, + PipTouchHandler pipTouchHandler, WindowManagerShellWrapper windowManagerShellWrapper) { + return Optional.ofNullable(PipController.create(context, displayController, + pipAppOpsListener, pipBoundsHandler, pipBoundsState, pipMediaController, + pipMenuActivityController, pipTaskOrganizer, pipTouchHandler, + windowManagerShellWrapper)); + } + + @WMSingleton @Provides static PipBoundsState providePipBoundsState() { return new PipBoundsState(); } - @SysUISingleton + @WMSingleton @Provides - static PipBoundsHandler providesPipBoundsHandler(Context context) { - return new PipBoundsHandler(context); + static PipBoundsHandler providesPipBoundsHandler(Context context, + PipBoundsState pipBoundsState) { + return new PipBoundsHandler(context, pipBoundsState); } - @SysUISingleton + @WMSingleton @Provides - static PipMenuActivityController providesPipMenuActivityController(Context context, + static PipMenuActivityController providePipMenuActivityController(Context context, PipMediaController pipMediaController, PipTaskOrganizer pipTaskOrganizer) { return new PipMenuActivityController(context, pipMediaController, pipTaskOrganizer); } - @SysUISingleton + @WMSingleton @Provides - static PipTouchHandler providesPipTouchHandler(Context context, + static PipTouchHandler providePipTouchHandler(Context context, PipMenuActivityController menuActivityController, PipBoundsHandler pipBoundsHandler, PipBoundsState pipBoundsState, PipTaskOrganizer pipTaskOrganizer, @@ -126,9 +121,9 @@ public class WMShellModule { pipBoundsState, pipTaskOrganizer, floatingContentCoordinator, pipUiEventLogger); } - @SysUISingleton + @WMSingleton @Provides - static PipTaskOrganizer providesPipTaskOrganizer(Context context, + static PipTaskOrganizer providePipTaskOrganizer(Context context, PipBoundsState pipBoundsState, PipBoundsHandler pipBoundsHandler, PipSurfaceTransactionHelper pipSurfaceTransactionHelper, diff --git a/packages/SystemUI/tests/AndroidManifest.xml b/packages/SystemUI/tests/AndroidManifest.xml index 2b4ed4e8f1cd..d541c8fbdff1 100644 --- a/packages/SystemUI/tests/AndroidManifest.xml +++ b/packages/SystemUI/tests/AndroidManifest.xml @@ -70,6 +70,13 @@ android:exported="false" android:resizeableActivity="true" /> + <activity android:name="com.android.systemui.emergency.EmergencyActivityTest" + android:exported="true"> + <intent-filter> + <action android:name="com.android.systemui.action.LAUNCH_EMERGENCY"/> + </intent-filter> + </activity> + <activity android:name="com.android.systemui.globalactions.GlobalActionsImeTest$TestActivity" android:excludeFromRecents="true" diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java index 3aa6ec08683f..094230db9d8c 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java @@ -24,6 +24,7 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.content.res.Resources; import android.test.suitebuilder.annotation.SmallTest; import android.testing.AndroidTestingRunner; import android.view.View; @@ -64,6 +65,8 @@ public class KeyguardClockSwitchControllerTest extends SysuiTestCase { ColorExtractor.GradientColors mGradientColors; @Mock KeyguardSliceViewController mKeyguardSliceViewController; + @Mock + Resources mResources; private KeyguardClockSwitchController mController; @@ -72,9 +75,13 @@ public class KeyguardClockSwitchControllerTest extends SysuiTestCase { MockitoAnnotations.initMocks(this); when(mView.isAttachedToWindow()).thenReturn(true); - + when(mResources.getString(anyInt())).thenReturn("h:mm"); mController = new KeyguardClockSwitchController( - mView, mStatusBarStateController, mColorExtractor, mClockManager, + mView, + mResources, + mStatusBarStateController, + mColorExtractor, + mClockManager, mKeyguardSliceViewController); when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE); diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPresentationTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPresentationTest.java index ae159c73b99f..62906f3656c7 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPresentationTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPresentationTest.java @@ -20,9 +20,11 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.when; import android.content.Context; +import android.hardware.display.DisplayManager; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.util.AttributeSet; +import android.view.Display; import android.view.LayoutInflater; import android.view.View; @@ -104,9 +106,10 @@ public class KeyguardPresentationTest extends SysuiTestCase { @Test public void testInflation_doesntCrash() { - KeyguardPresentation keyguardPresentation = new KeyguardPresentation(mContext, - mContext.getDisplayNoVerify(), mKeyguardStatusViewComponentFactory, - mLayoutInflater); + final Display display = mContext.getSystemService(DisplayManager.class).getDisplay( + Display.DEFAULT_DISPLAY); + KeyguardPresentation keyguardPresentation = new KeyguardPresentation(mContext, display, + mKeyguardStatusViewComponentFactory); keyguardPresentation.onCreate(null /*savedInstanceState */); } } diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewControllerTest.java index b7bcaa3c3566..9f8f6c1940ef 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewControllerTest.java @@ -44,9 +44,7 @@ import org.mockito.MockitoAnnotations; @RunWithLooper(setAsMainLooper = true) public class KeyguardSliceViewControllerTest extends SysuiTestCase { @Mock - private KeyguardSliceView mView;; - @Mock - private KeyguardStatusView mKeyguardStatusView; + private KeyguardSliceView mView; @Mock private TunerService mTunerService; @Mock @@ -63,7 +61,7 @@ public class KeyguardSliceViewControllerTest extends SysuiTestCase { when(mView.isAttachedToWindow()).thenReturn(true); when(mView.getContext()).thenReturn(mContext); mController = new KeyguardSliceViewController( - mView, mKeyguardStatusView, mActivityStarter, mConfigurationController, + mView, mActivityStarter, mConfigurationController, mTunerService, mDumpManager); mController.setupUri(KeyguardSliceProvider.KEYGUARD_SLICE_URI); } diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java new file mode 100644 index 000000000000..752a744826a1 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.keyguard; + +import static org.mockito.Mockito.verify; + +import android.test.suitebuilder.annotation.SmallTest; +import android.testing.AndroidTestingRunner; + +import com.android.systemui.SysuiTestCase; +import com.android.systemui.statusbar.policy.ConfigurationController; +import com.android.systemui.statusbar.policy.KeyguardStateController; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +@SmallTest +@RunWith(AndroidTestingRunner.class) +public class KeyguardStatusViewControllerTest extends SysuiTestCase { + + @Mock + private KeyguardStatusView mKeyguardStatusView; + @Mock + private KeyguardSliceViewController mKeyguardSliceViewController; + @Mock + private KeyguardClockSwitchController mKeyguardClockSwitchController; + @Mock + private KeyguardStateController mKeyguardStateController; + @Mock + private KeyguardUpdateMonitor mKeyguardUpdateMonitor; + @Mock + ConfigurationController mConfigurationController; + + private KeyguardStatusViewController mController; + + @Before + public void setup() { + MockitoAnnotations.initMocks(this); + + mController = new KeyguardStatusViewController( + mKeyguardStatusView, + mKeyguardSliceViewController, + mKeyguardClockSwitchController, + mKeyguardStateController, + mKeyguardUpdateMonitor, + mConfigurationController); + } + + @Test + public void dozeTimeTick_updatesSlice() { + mController.dozeTimeTick(); + verify(mKeyguardSliceViewController).refresh(); + } + + @Test + public void dozeTimeTick_updatesClock() { + mController.dozeTimeTick(); + verify(mKeyguardClockSwitchController).refresh(); + } +} diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewTest.java deleted file mode 100644 index 79ec4f2c553a..000000000000 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewTest.java +++ /dev/null @@ -1,69 +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.keyguard; - -import static org.mockito.Mockito.verify; - -import android.test.suitebuilder.annotation.SmallTest; -import android.testing.AndroidTestingRunner; -import android.testing.TestableLooper.RunWithLooper; -import android.view.LayoutInflater; - -import com.android.systemui.R; -import com.android.systemui.SysuiTestCase; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.InjectMocks; -import org.mockito.Mock; - -@SmallTest -@RunWithLooper -@RunWith(AndroidTestingRunner.class) -public class KeyguardStatusViewTest extends SysuiTestCase { - - @Mock - KeyguardSliceViewController mKeyguardSliceViewController; - - @Mock - KeyguardClockSwitch mClockView; - @InjectMocks - KeyguardStatusView mKeyguardStatusView; - - @Before - public void setUp() { - allowTestableLooperAsMainThread(); - LayoutInflater layoutInflater = LayoutInflater.from(getContext()); - mKeyguardStatusView = - (KeyguardStatusView) layoutInflater.inflate(R.layout.keyguard_status_view, null); - org.mockito.MockitoAnnotations.initMocks(this); - } - - @Test - public void dozeTimeTick_updatesSlice() { - mKeyguardStatusView.dozeTimeTick(); - verify(mKeyguardSliceViewController).refresh(); - } - - @Test - public void dozeTimeTick_updatesClock() { - mKeyguardStatusView.dozeTimeTick(); - verify(mClockView).refresh(); - } - -} diff --git a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java index ab805f42ab56..0c87f59af822 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java @@ -621,6 +621,44 @@ public class ScreenDecorationsTest extends SysuiTestCase { assertEquals(mScreenDecorations.mRoundedDefault, new Point(5, 5)); } + @Test + public void testOnlyRoundedCornerRadiusTop() { + mContext.getOrCreateTestableResources().addOverride( + com.android.internal.R.bool.config_fillMainBuiltInDisplayCutout, false); + mContext.getOrCreateTestableResources().addOverride( + com.android.internal.R.dimen.rounded_corner_radius, 0); + mContext.getOrCreateTestableResources().addOverride( + com.android.internal.R.dimen.rounded_corner_radius_top, 10); + mContext.getOrCreateTestableResources().addOverride( + com.android.internal.R.dimen.rounded_corner_radius_bottom, 0); + mContext.getOrCreateTestableResources() + .addOverride(R.bool.config_roundedCornerMultipleRadius, false); + + mScreenDecorations.start(); + assertEquals(new Point(0, 0), mScreenDecorations.mRoundedDefault); + assertEquals(new Point(10, 10), mScreenDecorations.mRoundedDefaultTop); + assertEquals(new Point(0, 0), mScreenDecorations.mRoundedDefaultBottom); + } + + @Test + public void testOnlyRoundedCornerRadiusBottom() { + mContext.getOrCreateTestableResources().addOverride( + com.android.internal.R.bool.config_fillMainBuiltInDisplayCutout, false); + mContext.getOrCreateTestableResources().addOverride( + com.android.internal.R.dimen.rounded_corner_radius, 0); + mContext.getOrCreateTestableResources().addOverride( + com.android.internal.R.dimen.rounded_corner_radius_top, 0); + mContext.getOrCreateTestableResources().addOverride( + com.android.internal.R.dimen.rounded_corner_radius_bottom, 20); + mContext.getOrCreateTestableResources() + .addOverride(R.bool.config_roundedCornerMultipleRadius, false); + + mScreenDecorations.start(); + assertEquals(new Point(0, 0), mScreenDecorations.mRoundedDefault); + assertEquals(new Point(0, 0), mScreenDecorations.mRoundedDefaultTop); + assertEquals(new Point(20, 20), mScreenDecorations.mRoundedDefaultBottom); + } + @Test public void testBoundingRectsToRegion() throws Exception { diff --git a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java index 86b1e61d76b1..4f3266d0db44 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java +++ b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java @@ -30,6 +30,7 @@ import android.testing.TestableLooper; import android.util.Log; import androidx.test.InstrumentationRegistry; +import androidx.test.uiautomator.UiDevice; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.settingslib.bluetooth.LocalBluetoothManager; @@ -72,7 +73,7 @@ public abstract class SysuiTestCase { @Before public void SysuiSetup() throws Exception { - SystemUIFactory.createFromConfig(mContext); + SystemUIFactory.createFromConfig(mContext, true); mDependency = new TestableDependency( SystemUIFactory.getInstance().getSysUIComponent().createDependency()); Dependency.setInstance(mDependency); @@ -149,6 +150,10 @@ public abstract class SysuiTestCase { return mContext; } + protected UiDevice getUiDevice() { + return UiDevice.getInstance(mRealInstrumentation); + } + protected void runShellCommand(String command) throws IOException { ParcelFileDescriptor pfd = mRealInstrumentation.getUiAutomation() .executeShellCommand(command); diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java index 3c248c73ab71..3da1f297dfe9 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java @@ -24,6 +24,7 @@ import static android.view.MotionEvent.ACTION_MOVE; import static android.view.MotionEvent.ACTION_UP; import static android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction.ACTION_CLICK; +import static com.android.systemui.accessibility.MagnificationModeSwitch.DEFAULT_FADE_OUT_ANIMATION_DELAY_MS; import static com.android.systemui.accessibility.MagnificationModeSwitch.FADING_ANIMATION_DURATION_MS; import static com.android.systemui.accessibility.MagnificationModeSwitch.getIconResId; @@ -33,6 +34,7 @@ import static org.hamcrest.CoreMatchers.hasItems; import static org.hamcrest.MatcherAssert.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyFloat; +import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doAnswer; @@ -48,6 +50,7 @@ import android.view.View; import android.view.ViewConfiguration; import android.view.ViewPropertyAnimator; import android.view.WindowManager; +import android.view.accessibility.AccessibilityManager; import android.view.accessibility.AccessibilityNodeInfo; import android.widget.ImageView; @@ -74,6 +77,8 @@ public class MagnificationModeSwitchTest extends SysuiTestCase { private ImageView mSpyImageView; @Mock + private AccessibilityManager mAccessibilityManager; + @Mock private WindowManager mWindowManager; @Mock private ViewPropertyAnimator mViewPropertyAnimator; @@ -89,6 +94,7 @@ public class MagnificationModeSwitchTest extends SysuiTestCase { wm.getMaximumWindowMetrics() ).when(mWindowManager).getMaximumWindowMetrics(); mContext.addMockSystemService(Context.WINDOW_SERVICE, mWindowManager); + mContext.addMockSystemService(Context.ACCESSIBILITY_SERVICE, mAccessibilityManager); mSpyImageView = Mockito.spy(new ImageView(mContext)); resetMockImageViewAndAnimator(); @@ -108,16 +114,37 @@ public class MagnificationModeSwitchTest extends SysuiTestCase { @Test public void showWindowModeButton_fullscreenMode_addViewAndSetImageResource() { mMagnificationModeSwitch.showButton(ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW); + verify(mSpyImageView).setImageResource( getIconResId(ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW)); + verify(mWindowManager).addView(eq(mSpyImageView), any(WindowManager.LayoutParams.class)); assertShowFadingAnimation(FADE_IN_ALPHA); assertShowFadingAnimation(FADE_OUT_ALPHA); + } - ArgumentCaptor<Runnable> captor = ArgumentCaptor.forClass(Runnable.class); - verify(mViewPropertyAnimator).withEndAction(captor.capture()); - verify(mWindowManager).addView(eq(mSpyImageView), any(WindowManager.LayoutParams.class)); + @Test + public void showMagnificationButton_a11yTimeout_autoFadeOut() { + final int a11yTimeout = 12345; + when(mAccessibilityManager.getRecommendedTimeoutMillis(anyInt(), anyInt())).thenReturn( + a11yTimeout); + + mMagnificationModeSwitch.showButton(ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW); - captor.getValue().run(); + verify(mAccessibilityManager).getRecommendedTimeoutMillis( + DEFAULT_FADE_OUT_ANIMATION_DELAY_MS, AccessibilityManager.FLAG_CONTENT_ICONS + | AccessibilityManager.FLAG_CONTENT_CONTROLS); + final ArgumentCaptor<Runnable> fadeOutCaptor = ArgumentCaptor.forClass(Runnable.class); + final ArgumentCaptor<Long> fadeOutDelay = ArgumentCaptor.forClass(Long.class); + verify(mSpyImageView).postOnAnimationDelayed(fadeOutCaptor.capture(), + fadeOutDelay.capture()); + assertEquals(a11yTimeout, (long) fadeOutDelay.getValue()); + + // Verify the end action after fade-out. + fadeOutCaptor.getValue().run(); + final ArgumentCaptor<Runnable> endActionCaptor = ArgumentCaptor.forClass(Runnable.class); + verify(mViewPropertyAnimator).withEndAction(endActionCaptor.capture()); + + endActionCaptor.getValue().run(); verify(mViewPropertyAnimator).cancel(); verify(mWindowManager).removeView(mSpyImageView); diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java index 5f2fd697b86d..f1606c5660ed 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java @@ -224,11 +224,12 @@ public class WindowMagnificationControllerTest extends SysuiTestCase { @Test - public void onDensityChanged_enabled_updateDimensionsAndLayout() { + public void onDensityChanged_enabled_updateDimensionsAndResetWindowMagnification() { mInstrumentation.runOnMainSync(() -> { mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN, Float.NaN); Mockito.reset(mWindowManager); + Mockito.reset(mMirrorWindowControl); }); mInstrumentation.runOnMainSync(() -> { @@ -237,7 +238,9 @@ public class WindowMagnificationControllerTest extends SysuiTestCase { verify(mResources, atLeastOnce()).getDimensionPixelSize(anyInt()); verify(mWindowManager).removeView(any()); + verify(mMirrorWindowControl).destroyControl(); verify(mWindowManager).addView(any(), any()); + verify(mMirrorWindowControl).showControl(); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java index b082d17e58ca..d9e9a8b26f0f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java @@ -45,6 +45,9 @@ import android.app.INotificationManager; import android.app.Notification; import android.app.PendingIntent; import android.content.pm.LauncherApps; +import android.content.res.Configuration; +import android.graphics.Insets; +import android.graphics.Rect; import android.hardware.display.AmbientDisplayConfiguration; import android.hardware.face.FaceManager; import android.os.Handler; @@ -80,10 +83,8 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.legacy.NotificationGroupManagerLegacy; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.NotificationTestHelper; -import com.android.systemui.statusbar.notification.row.dagger.NotificationShelfComponent; import com.android.systemui.statusbar.phone.DozeParameters; import com.android.systemui.statusbar.phone.KeyguardBypassController; -import com.android.systemui.statusbar.phone.LockscreenLockIconController; import com.android.systemui.statusbar.phone.NotificationShadeWindowControllerImpl; import com.android.systemui.statusbar.phone.NotificationShadeWindowView; import com.android.systemui.statusbar.phone.ShadeController; @@ -178,8 +179,6 @@ public class BubbleControllerTest extends SysuiTestCase { @Mock private ShadeController mShadeController; @Mock - private NotificationShelfComponent mNotificationShelfComponent; - @Mock private NotifPipeline mNotifPipeline; @Mock private FeatureFlags mFeatureFlagsOldPipeline; @@ -191,11 +190,12 @@ public class BubbleControllerTest extends SysuiTestCase { private IStatusBarService mStatusBarService; @Mock private LauncherApps mLauncherApps; - @Mock private LockscreenLockIconController mLockIconController; - - @Mock private WindowManagerShellWrapper mWindowManagerShellWrapper; - - @Mock private BubbleLogger mBubbleLogger; + @Mock + private WindowManagerShellWrapper mWindowManagerShellWrapper; + @Mock + private BubbleLogger mBubbleLogger; + @Mock + private BubblePositioner mPositioner; private BubbleData mBubbleData; @@ -210,7 +210,6 @@ public class BubbleControllerTest extends SysuiTestCase { mContext.addMockSystemService(FaceManager.class, mFaceManager); when(mColorExtractor.getNeutralColors()).thenReturn(mGradientColors); - // Bubbles get added to status bar window view mNotificationShadeWindowController = new NotificationShadeWindowControllerImpl(mContext, mWindowManager, mActivityManager, mDozeParameters, mStatusBarStateController, mConfigurationController, mKeyguardViewMediator, mKeyguardBypassController, @@ -241,6 +240,13 @@ public class BubbleControllerTest extends SysuiTestCase { mSysUiStateBubblesExpanded = (sysUiFlags & QuickStepContract.SYSUI_STATE_BUBBLES_EXPANDED) != 0); + mBubbleData = new BubbleData(mContext, mBubbleLogger); + + Rect availableRect = new Rect(0, 0, 1000, 5000); + when(mPositioner.getAvailableRect()).thenReturn(availableRect); + when(mPositioner.getOrientation()).thenReturn(Configuration.ORIENTATION_PORTRAIT); + when(mPositioner.getInsets()).thenReturn(Insets.of(0, 0, 0, 0)); + TestableNotificationInterruptStateProviderImpl interruptionStateProvider = new TestableNotificationInterruptStateProviderImpl(mContext.getContentResolver(), mock(PowerManager.class), @@ -252,7 +258,6 @@ public class BubbleControllerTest extends SysuiTestCase { mock(HeadsUpManager.class), mock(Handler.class) ); - mBubbleData = new BubbleData(mContext, mBubbleLogger); when(mFeatureFlagsOldPipeline.isNewNotifPipelineRenderingEnabled()).thenReturn(false); mBubbleController = new TestableBubbleController( mContext, @@ -279,7 +284,8 @@ public class BubbleControllerTest extends SysuiTestCase { mLauncherApps, mBubbleLogger, mock(Handler.class), - mock(ShellTaskOrganizer.class)); + mock(ShellTaskOrganizer.class), + mPositioner); mBubbleController.setExpandListener(mBubbleExpandListener); // Get a reference to the BubbleController's entry listener diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/MultiWindowTaskListenerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/MultiWindowTaskListenerTest.java deleted file mode 100644 index 7c1b41443e26..000000000000 --- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/MultiWindowTaskListenerTest.java +++ /dev/null @@ -1,178 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.bubbles; - -import static com.android.wm.shell.ShellTaskOrganizer.TASK_LISTENER_TYPE_MULTI_WINDOW; - -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyBoolean; -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 android.app.ActivityManager; -import android.os.Handler; -import android.testing.AndroidTestingRunner; -import android.testing.TestableLooper; -import android.view.SurfaceControl; -import android.window.WindowContainerToken; - -import androidx.test.filters.SmallTest; - -import com.android.systemui.SysuiTestCase; -import com.android.wm.shell.ShellTaskOrganizer; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -@SmallTest -@RunWith(AndroidTestingRunner.class) -@TestableLooper.RunWithLooper(setAsMainLooper = true) -// TODO: Place in com.android.wm.shell vs. com.android.wm.shell.bubbles on shell migration. -public class MultiWindowTaskListenerTest extends SysuiTestCase { - - @Mock - ShellTaskOrganizer mOrganizer; - @Mock - MultiWindowTaskListener.Listener mPendingListener; - @Mock - SurfaceControl mLeash; - @Mock - ActivityManager.RunningTaskInfo mTaskInfo; - @Mock - WindowContainerToken mToken; - - Handler mHandler; - MultiWindowTaskListener mTaskListener; - TestableLooper mTestableLooper; - - @Before - public void setUp() { - MockitoAnnotations.initMocks(this); - - mTestableLooper = TestableLooper.get(this); - mHandler = new Handler(mTestableLooper.getLooper()); - - mTaskInfo = new ActivityManager.RunningTaskInfo(); - mTaskInfo.token = mToken; - - mTaskListener = new MultiWindowTaskListener(mHandler, mOrganizer); - } - - private void addTaskAndVerify() { - mTaskListener.setPendingListener(mPendingListener); - mTaskListener.onTaskAppeared(mTaskInfo, mLeash); - mTestableLooper.processAllMessages(); - verify(mPendingListener).onTaskAppeared(eq(mTaskInfo), eq(mLeash)); - } - - @Test - public void testListenForMultiWindowMode() { - mTaskListener = new MultiWindowTaskListener(mHandler, mOrganizer); - verify(mOrganizer).addListener(eq(mTaskListener), eq(TASK_LISTENER_TYPE_MULTI_WINDOW)); - } - - @Test - public void testRemovePendingListener() { - addTaskAndVerify(); - reset(mPendingListener); - - mTaskListener.removeListener(mPendingListener); - - // If it was removed, our pendingListener shouldn't get triggered: - mTaskListener.onTaskAppeared(mTaskInfo, mLeash); - mTaskListener.onTaskInfoChanged(mTaskInfo); - mTaskListener.onBackPressedOnTaskRoot(mTaskInfo); - mTaskListener.onTaskVanished(mTaskInfo); - - mTestableLooper.processAllMessages(); - verify(mPendingListener, never()).onTaskAppeared(any(), any()); - verify(mPendingListener, never()).onTaskInfoChanged(any()); - verify(mPendingListener, never()).onBackPressedOnTaskRoot(any()); - verify(mPendingListener, never()).onTaskVanished(any()); - } - - @Test - public void testOnTaskAppeared() { - addTaskAndVerify(); - verify(mOrganizer).setInterceptBackPressedOnTaskRoot(eq(mToken), eq(true)); - } - - @Test - public void testOnTaskAppeared_nullListener() { - mTaskListener.onTaskAppeared(mTaskInfo, mLeash); - mTestableLooper.processAllMessages(); - - verify(mOrganizer, never()).setInterceptBackPressedOnTaskRoot(any(), anyBoolean()); - verify(mPendingListener, never()).onTaskAppeared(any(), any()); - } - - @Test - public void testOnTaskVanished() { - addTaskAndVerify(); - mTaskListener.onTaskVanished(mTaskInfo); - mTestableLooper.processAllMessages(); - - verify(mPendingListener).onTaskVanished(eq(mTaskInfo)); - } - - @Test - public void testOnTaskVanished_neverAdded() { - mTaskListener.onTaskVanished(mTaskInfo); - mTestableLooper.processAllMessages(); - - verify(mPendingListener, never()).onTaskVanished(any()); - } - - @Test - public void testOnTaskInfoChanged() { - addTaskAndVerify(); - mTaskListener.onTaskInfoChanged(mTaskInfo); - mTestableLooper.processAllMessages(); - - verify(mPendingListener).onTaskInfoChanged(eq(mTaskInfo)); - } - - @Test - public void testOnTaskInfoChanged_neverAdded() { - mTaskListener.onTaskInfoChanged(mTaskInfo); - mTestableLooper.processAllMessages(); - - verify(mPendingListener, never()).onTaskInfoChanged(any()); - } - - @Test - public void testOnBackPressedOnTaskRoot() { - addTaskAndVerify(); - mTaskListener.onBackPressedOnTaskRoot(mTaskInfo); - mTestableLooper.processAllMessages(); - - verify(mPendingListener).onBackPressedOnTaskRoot(eq(mTaskInfo)); - } - - @Test - public void testOnBackPressedOnTaskRoot_neverAdded() { - mTaskListener.onBackPressedOnTaskRoot(mTaskInfo); - mTestableLooper.processAllMessages(); - - verify(mPendingListener, never()).onBackPressedOnTaskRoot(any()); - } -} diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java index cbacd5393ccf..b9394ff3f26a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java @@ -42,7 +42,9 @@ import android.app.INotificationManager; import android.app.Notification; import android.app.PendingIntent; import android.content.pm.LauncherApps; -import android.content.res.Resources; +import android.content.res.Configuration; +import android.graphics.Insets; +import android.graphics.Rect; import android.hardware.display.AmbientDisplayConfiguration; import android.hardware.face.FaceManager; import android.os.Handler; @@ -170,8 +172,6 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { @Mock ColorExtractor.GradientColors mGradientColors; @Mock - private Resources mResources; - @Mock private ShadeController mShadeController; @Mock private NotificationShelfComponent mNotificationShelfComponent; @@ -191,6 +191,8 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { private WindowManagerShellWrapper mWindowManagerShellWrapper; @Mock private BubbleLogger mBubbleLogger; + @Mock + private BubblePositioner mPositioner; private BubbleData mBubbleData; @@ -223,7 +225,6 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { }, mLockIconController); - // Bubbles get added to status bar window view mNotificationShadeWindowController = new NotificationShadeWindowControllerImpl(mContext, mWindowManager, mActivityManager, mDozeParameters, mStatusBarStateController, mConfigurationController, mKeyguardViewMediator, mKeyguardBypassController, @@ -243,6 +244,13 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { mZenModeConfig.suppressedVisualEffects = 0; when(mZenModeController.getConfig()).thenReturn(mZenModeConfig); + mBubbleData = new BubbleData(mContext, mBubbleLogger); + + Rect availableRect = new Rect(0, 0, 1000, 5000); + when(mPositioner.getAvailableRect()).thenReturn(availableRect); + when(mPositioner.getOrientation()).thenReturn(Configuration.ORIENTATION_PORTRAIT); + when(mPositioner.getInsets()).thenReturn(Insets.of(0, 0, 0, 0)); + TestableNotificationInterruptStateProviderImpl interruptionStateProvider = new TestableNotificationInterruptStateProviderImpl(mContext.getContentResolver(), mock(PowerManager.class), @@ -254,7 +262,6 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { mock(HeadsUpManager.class), mock(Handler.class) ); - mBubbleData = new BubbleData(mContext, mBubbleLogger); when(mFeatureFlagsNewPipeline.isNewNotifPipelineRenderingEnabled()).thenReturn(true); mBubbleController = new TestableBubbleController( mContext, @@ -281,7 +288,8 @@ public class NewNotifPipelineBubbleControllerTest extends SysuiTestCase { mLauncherApps, mBubbleLogger, mock(Handler.class), - mock(ShellTaskOrganizer.class)); + mock(ShellTaskOrganizer.class), + mPositioner); mBubbleController.addNotifCallback(mNotifCallback); mBubbleController.setExpandListener(mBubbleExpandListener); diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/TaskViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/TaskViewTest.java index 6f3968dc5819..b13c6fc0cad4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/TaskViewTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/TaskViewTest.java @@ -25,6 +25,7 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; @@ -35,6 +36,7 @@ import android.app.ActivityManager; import android.app.ActivityOptions; import android.app.PendingIntent; import android.content.Context; +import android.os.Handler; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.view.SurfaceControl; @@ -46,6 +48,7 @@ import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; import com.android.wm.shell.ShellTaskOrganizer; +import com.android.wm.shell.common.HandlerExecutor; import org.junit.After; import org.junit.Before; @@ -53,6 +56,7 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import org.mockito.invocation.InvocationOnMock; @SmallTest @RunWith(AndroidTestingRunner.class) @@ -69,7 +73,7 @@ public class TaskViewTest extends SysuiTestCase { @Mock ShellTaskOrganizer mOrganizer; @Mock - MultiWindowTaskListener mTaskListener; + HandlerExecutor mExecutor; SurfaceSession mSession; SurfaceControl mLeash; @@ -86,13 +90,18 @@ public class TaskViewTest extends SysuiTestCase { mContext = getContext(); - when(mTaskListener.getTaskOrganizer()).thenReturn(mOrganizer); mTaskInfo = new ActivityManager.RunningTaskInfo(); mTaskInfo.token = mToken; mTaskInfo.taskId = 314; mTaskInfo.taskDescription = mock(ActivityManager.TaskDescription.class); - mTaskView = new TaskView(mContext, mTaskListener); + doAnswer((InvocationOnMock invocationOnMock) -> { + final Runnable r = invocationOnMock.getArgument(0); + r.run(); + return null; + }).when(mExecutor).execute(any()); + + mTaskView = new TaskView(mContext, mOrganizer, mExecutor); mTaskView.setListener(mViewListener); } @@ -105,7 +114,7 @@ public class TaskViewTest extends SysuiTestCase { @Test public void testSetPendingListener_throwsException() { - TaskView taskView = new TaskView(mContext, mTaskListener); + TaskView taskView = new TaskView(mContext, mOrganizer, mExecutor); taskView.setListener(mViewListener); try { taskView.setListener(mViewListener); @@ -121,7 +130,7 @@ public class TaskViewTest extends SysuiTestCase { ActivityOptions options = ActivityOptions.makeBasic(); mTaskView.startActivity(mock(PendingIntent.class), null, options); - verify(mTaskListener).setPendingListener(eq(mTaskView)); + verify(mOrganizer).setPendingLaunchCookieListener(any(), eq(mTaskView)); assertThat(options.getLaunchWindowingMode()).isEqualTo(WINDOWING_MODE_MULTI_WINDOW); assertThat(options.getTaskAlwaysOnTop()).isTrue(); } @@ -189,7 +198,7 @@ public class TaskViewTest extends SysuiTestCase { mTaskView.surfaceCreated(mock(SurfaceHolder.class)); mTaskView.release(); - verify(mTaskListener).removeListener(eq(mTaskView)); + verify(mOrganizer).removeListener(eq(mTaskView)); verify(mViewListener).onReleased(); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableBubbleController.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableBubbleController.java index 27c6fc147772..aaeee16dc1fd 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableBubbleController.java +++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableBubbleController.java @@ -70,7 +70,8 @@ public class TestableBubbleController extends BubbleController { LauncherApps launcherApps, BubbleLogger bubbleLogger, Handler mainHandler, - ShellTaskOrganizer shellTaskOrganizer) { + ShellTaskOrganizer shellTaskOrganizer, + BubblePositioner positioner) { super(context, notificationShadeWindowController, statusBarStateController, shadeController, data, Runnable::run, configurationController, interruptionStateProvider, @@ -78,7 +79,7 @@ public class TestableBubbleController extends BubbleController { notifPipeline, featureFlags, dumpManager, floatingContentCoordinator, dataRepository, sysUiState, notificationManager, statusBarService, windowManager, windowManagerShellWrapper, launcherApps, bubbleLogger, - mainHandler, shellTaskOrganizer); + mainHandler, shellTaskOrganizer, positioner); setInflateSynchronously(true); } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/ExpandedAnimationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/ExpandedAnimationControllerTest.java index 6a1486382eac..a5bb8ea23559 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/ExpandedAnimationControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/ExpandedAnimationControllerTest.java @@ -17,26 +17,30 @@ package com.android.systemui.bubbles.animation; import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; +import android.annotation.SuppressLint; import android.content.res.Configuration; import android.content.res.Resources; -import android.graphics.Point; +import android.graphics.Insets; import android.graphics.PointF; +import android.graphics.Rect; import android.testing.AndroidTestingRunner; import android.view.View; +import android.view.WindowManager; import android.widget.FrameLayout; import androidx.dynamicanimation.animation.DynamicAnimation; import androidx.test.filters.SmallTest; import com.android.systemui.R; +import com.android.systemui.bubbles.BubblePositioner; import org.junit.Before; import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; -import org.mockito.Mockito; import org.mockito.Spy; @SmallTest @@ -46,47 +50,45 @@ public class ExpandedAnimationControllerTest extends PhysicsAnimationLayoutTestC private int mDisplayWidth = 500; private int mDisplayHeight = 1000; private int mExpandedViewPadding = 10; - private int mOrientation = Configuration.ORIENTATION_PORTRAIT; - private float mLauncherGridDiff = 30f; - - private Runnable mOnBubbleAnimatedOutAction = Mockito.mock(Runnable.class); + private Runnable mOnBubbleAnimatedOutAction = mock(Runnable.class); @Spy - private ExpandedAnimationController mExpandedController = - new ExpandedAnimationController( - new Point(mDisplayWidth, mDisplayHeight) /* displaySize */, - mExpandedViewPadding, mOrientation, mOnBubbleAnimatedOutAction); + ExpandedAnimationController mExpandedController; private int mStackOffset; - private float mBubblePaddingTop; - private float mBubbleSize; - private PointF mExpansionPoint; + @SuppressLint("VisibleForTests") @Before public void setUp() throws Exception { super.setUp(); + + BubblePositioner positioner = new BubblePositioner(getContext(), mock(WindowManager.class)); + positioner.update(Configuration.ORIENTATION_PORTRAIT, + Insets.of(0, 0, 0, 0), + new Rect(0, 0, mDisplayWidth, mDisplayHeight)); + mExpandedController = new ExpandedAnimationController(positioner, mExpandedViewPadding, + mOnBubbleAnimatedOutAction); + addOneMoreThanBubbleLimitBubbles(); mLayout.setActiveController(mExpandedController); Resources res = mLayout.getResources(); mStackOffset = res.getDimensionPixelSize(R.dimen.bubble_stack_offset); - mBubblePaddingTop = res.getDimensionPixelSize(R.dimen.bubble_padding_top); - mBubbleSize = res.getDimensionPixelSize(R.dimen.individual_bubble_size); mExpansionPoint = new PointF(100, 100); } @Test @Ignore public void testExpansionAndCollapse() throws InterruptedException { - Runnable afterExpand = Mockito.mock(Runnable.class); + Runnable afterExpand = mock(Runnable.class); mExpandedController.expandFromStack(afterExpand); waitForPropertyAnimations(DynamicAnimation.TRANSLATION_X, DynamicAnimation.TRANSLATION_Y); testBubblesInCorrectExpandedPositions(); verify(afterExpand).run(); - Runnable afterCollapse = Mockito.mock(Runnable.class); + Runnable afterCollapse = mock(Runnable.class); mExpandedController.collapseBackToStack(mExpansionPoint, afterCollapse); waitForPropertyAnimations(DynamicAnimation.TRANSLATION_X, DynamicAnimation.TRANSLATION_Y); @@ -121,7 +123,7 @@ public class ExpandedAnimationControllerTest extends PhysicsAnimationLayoutTestC /** Expand the stack and wait for animations to finish. */ private void expand() throws InterruptedException { - mExpandedController.expandFromStack(Mockito.mock(Runnable.class)); + mExpandedController.expandFromStack(mock(Runnable.class)); waitForPropertyAnimations(DynamicAnimation.TRANSLATION_X, DynamicAnimation.TRANSLATION_Y); } @@ -141,51 +143,12 @@ public class ExpandedAnimationControllerTest extends PhysicsAnimationLayoutTestC private void testBubblesInCorrectExpandedPositions() { // Check all the visible bubbles to see if they're in the right place. for (int i = 0; i < mLayout.getChildCount(); i++) { - assertEquals(getBubbleLeft(i), + float expectedPosition = mExpandedController.getBubbleXOrYForOrientation(i); + assertEquals(expectedPosition, mLayout.getChildAt(i).getTranslationX(), 2f); - assertEquals(mExpandedController.getExpandedY(), + assertEquals(expectedPosition, mLayout.getChildAt(i).getTranslationY(), 2f); } } - - /** - * @param index Bubble index in row. - * @return Bubble left x from left edge of screen. - */ - public float getBubbleLeft(int index) { - final float bubbleLeft = index * (mBubbleSize + getSpaceBetweenBubbles()); - return getRowLeft() + bubbleLeft; - } - - private float getRowLeft() { - if (mLayout == null) { - return 0; - } - int bubbleCount = mLayout.getChildCount(); - final float totalBubbleWidth = bubbleCount * mBubbleSize; - final float totalGapWidth = (bubbleCount - 1) * getSpaceBetweenBubbles(); - final float rowWidth = totalGapWidth + totalBubbleWidth; - - final float centerScreen = mDisplayWidth / 2f; - final float halfRow = rowWidth / 2f; - final float rowLeft = centerScreen - halfRow; - - return rowLeft; - } - - /** - * @return Space between bubbles in row above expanded view. - */ - private float getSpaceBetweenBubbles() { - final float rowMargins = (mExpandedViewPadding + mLauncherGridDiff) * 2; - final float maxRowWidth = mDisplayWidth - rowMargins; - - final float totalBubbleWidth = mMaxBubbles * mBubbleSize; - final float totalGapWidth = maxRowWidth - totalBubbleWidth; - - final int gapCount = mMaxBubbles - 1; - final float gapWidth = totalGapWidth / gapCount; - return gapWidth; - } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/StackAnimationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/StackAnimationControllerTest.java index 9242ce940bcd..7d0abec79de1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/StackAnimationControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/StackAnimationControllerTest.java @@ -18,6 +18,7 @@ package com.android.systemui.bubbles.animation; import static org.junit.Assert.assertEquals; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; @@ -33,6 +34,7 @@ import androidx.dynamicanimation.animation.SpringForce; import androidx.test.filters.SmallTest; import com.android.systemui.R; +import com.android.systemui.bubbles.BubblePositioner; import com.android.wm.shell.common.FloatingContentCoordinator; import org.junit.Before; @@ -40,7 +42,6 @@ import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; -import org.mockito.Mockito; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @@ -67,7 +68,7 @@ public class StackAnimationControllerTest extends PhysicsAnimationLayoutTestCase public int getAsInt() { return mLayout.getChildCount(); } - }, Mockito.mock(Runnable.class))); + }, mock(Runnable.class))); mLayout.setActiveController(mStackController); addOneMoreThanBubbleLimitBubbles(); mStackOffset = mLayout.getResources().getDimensionPixelSize(R.dimen.bubble_stack_offset); @@ -306,7 +307,10 @@ public class StackAnimationControllerTest extends PhysicsAnimationLayoutTestCase FloatingContentCoordinator floatingContentCoordinator, IntSupplier bubbleCountSupplier, Runnable onBubbleAnimatedOutAction) { - super(floatingContentCoordinator, bubbleCountSupplier, onBubbleAnimatedOutAction); + super(floatingContentCoordinator, + bubbleCountSupplier, + onBubbleAnimatedOutAction, + mock(BubblePositioner.class)); } @Override diff --git a/packages/SystemUI/tests/src/com/android/systemui/emergency/EmergencyActivityTest.java b/packages/SystemUI/tests/src/com/android/systemui/emergency/EmergencyActivityTest.java new file mode 100644 index 000000000000..a52a598ee7ec --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/emergency/EmergencyActivityTest.java @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.emergency; + +import android.app.Activity; +import android.os.Bundle; + +import com.android.systemui.R; + +/** + * Test activity for resolving {@link EmergencyGesture#ACTION_LAUNCH_EMERGENCY} action. + */ +public class EmergencyActivityTest extends Activity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.main); + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java index b4af786c5579..78ee5936fe0b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java @@ -53,6 +53,9 @@ import android.view.WindowManagerPolicyConstants; import android.widget.FrameLayout; import androidx.test.filters.SmallTest; +import androidx.test.uiautomator.By; +import androidx.test.uiautomator.UiObject2; +import androidx.test.uiautomator.Until; import com.android.internal.colorextraction.ColorExtractor; import com.android.internal.logging.MetricsLogger; @@ -86,11 +89,16 @@ import org.mockito.MockitoAnnotations; import java.util.List; import java.util.concurrent.Executor; +import java.util.regex.Pattern; @SmallTest @RunWith(AndroidTestingRunner.class) @TestableLooper.RunWithLooper(setAsMainLooper = true) public class GlobalActionsDialogTest extends SysuiTestCase { + private static final long UI_TIMEOUT_MILLIS = 5000; // 5 sec + private static final Pattern CANCEL_BUTTON = + Pattern.compile("cancel", Pattern.CASE_INSENSITIVE); + private GlobalActionsDialog mGlobalActionsDialog; @Mock private GlobalActions.GlobalActionsManager mWindowManagerFuncs; @@ -240,6 +248,13 @@ public class GlobalActionsDialogTest extends SysuiTestCase { mGlobalActionsDialog.makeScreenshotActionForTesting(); screenshotAction.onLongPress(); verifyLogPosted(GlobalActionsDialog.GlobalActionsEvent.GA_SCREENSHOT_LONG_PRESS); + + // Dismiss ScreenRecordDialog opened by the long press above. + final UiObject2 cancelButton = getUiDevice().wait( + Until.findObject(By.text(CANCEL_BUTTON)), UI_TIMEOUT_MILLIS); + if (cancelButton != null) { + cancelButton.click(); + } } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsImeTest.java b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsImeTest.java index 58959c456cd9..bfc793516c8d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsImeTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsImeTest.java @@ -16,6 +16,7 @@ package com.android.systemui.globalactions; +import static android.provider.Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD; import static android.view.WindowInsets.Type.ime; import static org.junit.Assert.assertEquals; @@ -24,9 +25,11 @@ import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import android.app.Activity; +import android.content.ContentResolver; import android.os.Bundle; import android.os.PowerManager; import android.os.SystemClock; +import android.provider.Settings; import android.view.View; import android.view.WindowInsets; import android.view.WindowInsetsController; @@ -41,6 +44,7 @@ import androidx.test.rule.ActivityTestRule; import com.android.systemui.SysuiTestCase; import org.junit.After; +import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -54,8 +58,23 @@ public class GlobalActionsImeTest extends SysuiTestCase { public ActivityTestRule<TestActivity> mActivityTestRule = new ActivityTestRule<>( TestActivity.class, false, false); + private int mOriginalShowImeWithHardKeyboard; + + @Before + public void setUp() { + final ContentResolver contentResolver = mContext.getContentResolver(); + mOriginalShowImeWithHardKeyboard = Settings.Secure.getInt( + contentResolver, SHOW_IME_WITH_HARD_KEYBOARD, 0); + // Forcibly shows IME even when hardware keyboard is connected. + // To change USER_SYSTEM settings, we have to use settings shell command. + executeShellCommand("settings put secure " + SHOW_IME_WITH_HARD_KEYBOARD + " 1"); + } + @After public void tearDown() { + // To restore USER_SYSTEM settings, we have to use settings shell command. + executeShellCommand("settings put secure " + + SHOW_IME_WITH_HARD_KEYBOARD + " " + mOriginalShowImeWithHardKeyboard); executeShellCommand("input keyevent HOME"); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt index 182a056be557..af677c9d9f64 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt @@ -111,9 +111,11 @@ public class MediaControlPanelTest : SysuiTestCase() { private lateinit var action2: ImageButton private lateinit var action3: ImageButton private lateinit var action4: ImageButton + private lateinit var settingsText: TextView private lateinit var settings: View private lateinit var cancel: View - private lateinit var dismiss: View + private lateinit var dismiss: FrameLayout + private lateinit var dismissLabel: View private lateinit var session: MediaSession private val device = MediaDeviceData(true, null, DEVICE_NAME) @@ -171,12 +173,16 @@ public class MediaControlPanelTest : SysuiTestCase() { whenever(holder.action3).thenReturn(action3) action4 = ImageButton(context) whenever(holder.action4).thenReturn(action4) + settingsText = TextView(context) + whenever(holder.settingsText).thenReturn(settingsText) settings = View(context) whenever(holder.settings).thenReturn(settings) cancel = View(context) whenever(holder.cancel).thenReturn(cancel) - dismiss = View(context) + dismiss = FrameLayout(context) whenever(holder.dismiss).thenReturn(dismiss) + dismissLabel = View(context) + whenever(holder.dismissLabel).thenReturn(dismissLabel) // Create media session val metadataBuilder = MediaMetadata.Builder().apply { @@ -330,6 +336,7 @@ public class MediaControlPanelTest : SysuiTestCase() { notificationKey = KEY) player.bind(state, mediaKey) + assertThat(dismiss.isEnabled).isEqualTo(true) dismiss.callOnClick() val captor = ArgumentCaptor.forClass(ActivityStarter.OnDismissAction::class.java) verify(keyguardDismissUtil).executeWhenUnlocked(captor.capture(), anyBoolean()) @@ -337,4 +344,16 @@ public class MediaControlPanelTest : SysuiTestCase() { captor.value.onDismiss() verify(mediaDataManager).dismissMediaData(eq(mediaKey), anyLong()) } + + @Test + fun dismissButtonDisabled() { + val mediaKey = "key for dismissal" + player.attach(holder) + val state = MediaData(USER_ID, true, BG_COLOR, APP, null, ARTIST, TITLE, null, emptyList(), + emptyList(), PACKAGE, session.getSessionToken(), null, null, true, null, + isClearable = false, notificationKey = KEY) + player.bind(state, mediaKey) + + assertThat(dismiss.isEnabled).isEqualTo(false) + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/SeekBarViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/SeekBarViewModelTest.kt index b81ab74458ce..1f9862c07a4c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/SeekBarViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/SeekBarViewModelTest.kt @@ -654,4 +654,21 @@ public class SeekBarViewModelTest : SysuiTestCase() { fakeExecutor.runAllReady() verify(mockController).unregisterCallback(any()) } + + @Test + fun nullPlaybackStateUnregistersCallback() { + viewModel.updateController(mockController) + val captor = ArgumentCaptor.forClass(MediaController.Callback::class.java) + verify(mockController).registerCallback(captor.capture()) + val callback = captor.value + // WHEN the callback receives a null state + callback.onPlaybackStateChanged(null) + with(fakeExecutor) { + advanceClockToNext() + runAllReady() + } + // THEN we unregister callback (as a result of clearing the controller) + fakeExecutor.runAllReady() + verify(mockController).unregisterCallback(any()) + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerFlagsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerFlagsTest.kt index cb17829ddaf8..cd94f8444c45 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerFlagsTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerFlagsTest.kt @@ -54,6 +54,7 @@ class PrivacyItemControllerFlagsTest : SysuiTestCase() { private const val ALL_INDICATORS = SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED private const val MIC_CAMERA = SystemUiDeviceConfigFlags.PROPERTY_MIC_CAMERA_ENABLED + private const val LOCATION = SystemUiDeviceConfigFlags.PROPERTY_LOCATION_INDICATORS_ENABLED } @Mock @@ -116,6 +117,15 @@ class PrivacyItemControllerFlagsTest : SysuiTestCase() { } @Test + fun testLocationChanged() { + changeLocation(true) + executor.runAllReady() + + verify(callback).onFlagLocationChanged(true) + assertTrue(privacyItemController.locationAvailable) + } + + @Test fun testAllChanged() { changeAll(true) executor.runAllReady() @@ -158,6 +168,14 @@ class PrivacyItemControllerFlagsTest : SysuiTestCase() { } @Test + fun testLocation_listening() { + changeLocation(true) + executor.runAllReady() + + verify(appOpsController).addCallback(eq(PrivacyItemController.OPS), any()) + } + + @Test @Ignore // TODO(b/168209929) fun testAllFalse_notListening() { changeAll(true) @@ -205,6 +223,7 @@ class PrivacyItemControllerFlagsTest : SysuiTestCase() { } private fun changeMicCamera(value: Boolean?) = changeProperty(MIC_CAMERA, value) + private fun changeLocation(value: Boolean?) = changeProperty(LOCATION, value) private fun changeAll(value: Boolean?) = changeProperty(ALL_INDICATORS, value) private fun changeProperty(name: String, value: Boolean?) { diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSDetailTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSDetailTest.java index 353efeeb21f6..803919200150 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSDetailTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSDetailTest.java @@ -70,7 +70,7 @@ public class QSDetailTest extends SysuiTestCase { mQsDetail = (QSDetail) LayoutInflater.from(mContext).inflate(R.layout.qs_detail, null); mQsPanel = mock(QSPanel.class); mQuickHeader = mock(QuickStatusBarHeader.class); - mQsDetail.setQsPanel(mQsPanel, mQuickHeader, mock(View.class)); + mQsDetail.setQsPanel(mQsPanel, mQuickHeader, mock(QSFooter.class)); mMockDetailAdapter = mock(DetailAdapter.class); when(mMockDetailAdapter.createDetailView(any(), any(), any())) diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterImplTest.java deleted file mode 100644 index 99f2d8042547..000000000000 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterImplTest.java +++ /dev/null @@ -1,109 +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.qs; - -import static com.google.common.truth.Truth.assertThat; - -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyBoolean; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.content.ClipData; -import android.content.ClipboardManager; -import android.testing.AndroidTestingRunner; -import android.testing.TestableLooper; -import android.testing.TestableLooper.RunWithLooper; -import android.view.LayoutInflater; -import android.view.View; -import android.widget.TextView; - -import androidx.test.filters.SmallTest; - -import com.android.systemui.R; -import com.android.systemui.R.id; -import com.android.systemui.plugins.ActivityStarter; -import com.android.systemui.settings.UserTracker; -import com.android.systemui.statusbar.policy.DeviceProvisionedController; -import com.android.systemui.statusbar.policy.UserInfoController; -import com.android.systemui.utils.leaks.LeakCheckedTest; - -import org.junit.Before; -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; - -@RunWith(AndroidTestingRunner.class) -@RunWithLooper -@SmallTest -public class QSFooterImplTest extends LeakCheckedTest { - - private QSFooterImpl mFooter; - private ActivityStarter mActivityStarter; - private DeviceProvisionedController mDeviceProvisionedController; - private UserInfoController mUserInfoController; - private UserTracker mUserTracker; - @Mock - private ClipboardManager mClipboardManager; - - @Before - public void setup() throws Exception { - MockitoAnnotations.initMocks(this); - - injectLeakCheckedDependencies(ALL_SUPPORTED_CLASSES); - mActivityStarter = mDependency.injectMockDependency(ActivityStarter.class); - mDeviceProvisionedController = mDependency.injectMockDependency( - DeviceProvisionedController.class); - mUserInfoController = mDependency.injectMockDependency(UserInfoController.class); - mUserTracker = mDependency.injectMockDependency(UserTracker.class); - - mContext.addMockSystemService(ClipboardManager.class, mClipboardManager); - - when(mUserTracker.getUserContext()).thenReturn(mContext); - - TestableLooper.get(this).runWithLooper( - () -> mFooter = (QSFooterImpl) LayoutInflater.from(mContext).inflate( - R.layout.qs_footer_impl, null)); - } - - @Test - public void testBuildTextCopy() { - TextView buildTextView = mFooter.requireViewById(R.id.build); - CharSequence buildText = "TEST"; - buildTextView.setText(buildText); - buildTextView.setLongClickable(true); - - buildTextView.performLongClick(); - - ArgumentCaptor<ClipData> captor = ArgumentCaptor.forClass(ClipData.class); - verify(mClipboardManager).setPrimaryClip(captor.capture()); - assertThat(captor.getValue().getItemAt(0).getText()).isEqualTo(buildText); - } - - @Test - @Ignore("failing") - public void testSettings_UserNotSetup() { - View settingsButton = mFooter.findViewById(id.settings_button); - when(mDeviceProvisionedController.isCurrentUserSetup()).thenReturn(false); - - mFooter.onClick(settingsButton); - // Verify Settings wasn't launched. - verify(mActivityStarter, never()).startActivity(any(), anyBoolean()); - } -} diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterViewControllerTest.java new file mode 100644 index 000000000000..065f236d19b3 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterViewControllerTest.java @@ -0,0 +1,144 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.content.ClipData; +import android.content.ClipboardManager; +import android.os.UserManager; +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; +import android.view.View; +import android.widget.TextView; + +import androidx.test.filters.SmallTest; + +import com.android.internal.logging.MetricsLogger; +import com.android.internal.logging.testing.FakeMetricsLogger; +import com.android.systemui.Dependency; +import com.android.systemui.R; +import com.android.systemui.plugins.ActivityStarter; +import com.android.systemui.settings.UserTracker; +import com.android.systemui.statusbar.phone.SettingsButton; +import com.android.systemui.statusbar.policy.DeviceProvisionedController; +import com.android.systemui.statusbar.policy.UserInfoController; +import com.android.systemui.tuner.TunerService; +import com.android.systemui.utils.leaks.FakeTunerService; +import com.android.systemui.utils.leaks.LeakCheckedTest; + +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; + +@RunWith(AndroidTestingRunner.class) +@TestableLooper.RunWithLooper +@SmallTest +public class QSFooterViewControllerTest extends LeakCheckedTest { + + @Mock + private QSFooterView mView; + @Mock + private UserManager mUserManager; + @Mock + private ActivityStarter mActivityStarter; + @Mock + private DeviceProvisionedController mDeviceProvisionedController; + @Mock + private UserInfoController mUserInfoController; + @Mock + private UserTracker mUserTracker; + @Mock + private QSPanelController mQSPanelController; + @Mock + private ClipboardManager mClipboardManager; + private FakeTunerService mFakeTunerService; + private MetricsLogger mMetricsLogger = new FakeMetricsLogger(); + + @Mock + private SettingsButton mSettingsButton; + @Mock + private TextView mBuildText; + @Mock + private View mEdit; + + private QSFooterViewController mController; + + @Before + public void setup() throws Exception { + MockitoAnnotations.initMocks(this); + + injectLeakCheckedDependencies(ALL_SUPPORTED_CLASSES); + + mFakeTunerService = (FakeTunerService) Dependency.get(TunerService.class); + + mContext.addMockSystemService(ClipboardManager.class, mClipboardManager); + + when(mView.getContext()).thenReturn(mContext); + when(mView.getResources()).thenReturn(mContext.getResources()); + when(mUserTracker.getUserContext()).thenReturn(mContext); + + when(mView.isAttachedToWindow()).thenReturn(true); + when(mView.findViewById(R.id.settings_button)).thenReturn(mSettingsButton); + when(mView.findViewById(R.id.build)).thenReturn(mBuildText); + when(mView.findViewById(android.R.id.edit)).thenReturn(mEdit); + + mController = new QSFooterViewController(mView, mUserManager, mUserInfoController, + mActivityStarter, mDeviceProvisionedController, mUserTracker, mQSPanelController, + mFakeTunerService, mMetricsLogger); + + mController.init(); + } + + @Test + public void testBuildTextCopy() { + String text = "TEST"; + ArgumentCaptor<View.OnLongClickListener> onLongClickCaptor = + ArgumentCaptor.forClass(View.OnLongClickListener.class); + + verify(mBuildText).setOnLongClickListener(onLongClickCaptor.capture()); + + when(mBuildText.getText()).thenReturn(text); + onLongClickCaptor.getValue().onLongClick(mBuildText); + + ArgumentCaptor<ClipData> captor = ArgumentCaptor.forClass(ClipData.class); + verify(mClipboardManager).setPrimaryClip(captor.capture()); + assertThat(captor.getValue().getItemAt(0).getText()).isEqualTo(text); + } + + @Test + public void testSettings_UserNotSetup() { + ArgumentCaptor<View.OnClickListener> onClickCaptor = + ArgumentCaptor.forClass(View.OnClickListener.class); + verify(mSettingsButton).setOnClickListener(onClickCaptor.capture()); + + when(mDeviceProvisionedController.isCurrentUserSetup()).thenReturn(false); + + onClickCaptor.getValue().onClick(mSettingsButton); + // Verify Settings wasn't launched. + verify(mActivityStarter, never()).startActivity(any(), anyBoolean()); + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java index e472cb22ed6b..90609ccb48d6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java @@ -16,7 +16,9 @@ package com.android.systemui.qs; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; import android.app.Fragment; import android.content.Context; @@ -42,6 +44,7 @@ import com.android.systemui.SysuiBaseFragmentTest; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.dump.DumpManager; import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.qs.dagger.QSFragmentComponent; import com.android.systemui.qs.logging.QSLogger; import com.android.systemui.qs.tileimpl.QSFactoryImpl; import com.android.systemui.settings.UserTracker; @@ -61,6 +64,8 @@ import org.junit.Before; import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; import java.util.Optional; @@ -71,6 +76,12 @@ import java.util.Optional; public class QSFragmentTest extends SysuiBaseFragmentTest { private MetricsLogger mMockMetricsLogger; + @Mock + private QSFragmentComponent.Factory mQsComponentFactory; + @Mock + private QSFragmentComponent mQsFragmentComponent; + @Mock + private QSPanelController mQSPanelController; public QSFragmentTest() { super(QSFragment.class); @@ -80,6 +91,10 @@ public class QSFragmentTest extends SysuiBaseFragmentTest { @Before @Ignore("failing") public void addLeakCheckDependencies() { + MockitoAnnotations.initMocks(this); + when(mQsComponentFactory.create(any(QSFragment.class))).thenReturn(mQsFragmentComponent); + when(mQsFragmentComponent.getQSPanelController()).thenReturn(mQSPanelController); + mMockMetricsLogger = mDependency.injectMockDependency(MetricsLogger.class); mContext.addMockSystemService(Context.LAYOUT_INFLATER_SERVICE, new LayoutInflaterBuilder(mContext) @@ -152,6 +167,6 @@ public class QSFragmentTest extends SysuiBaseFragmentTest { mock(QSTileHost.class), mock(StatusBarStateController.class), commandQueue, - mock(QSContainerImplController.Builder.class)); + mQsComponentFactory); } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java new file mode 100644 index 000000000000..bf0e0841de9a --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java @@ -0,0 +1,141 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs; + +import static org.junit.Assert.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper.RunWithLooper; + +import androidx.test.filters.SmallTest; + +import com.android.internal.logging.MetricsLogger; +import com.android.internal.logging.UiEventLogger; +import com.android.internal.logging.nano.MetricsProto.MetricsEvent; +import com.android.internal.logging.testing.UiEventLoggerFake; +import com.android.systemui.SysuiTestCase; +import com.android.systemui.dump.DumpManager; +import com.android.systemui.media.MediaHost; +import com.android.systemui.plugins.qs.QSTileView; +import com.android.systemui.qs.tileimpl.QSTileImpl; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.io.FileDescriptor; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.util.Collections; + +@RunWith(AndroidTestingRunner.class) +@RunWithLooper +@SmallTest +public class QSPanelControllerBaseTest extends SysuiTestCase { + + @Mock + private QSPanel mQSPanel; + @Mock + private QSTileHost mQSTileHost; + @Mock + private MediaHost mMediaHost; + @Mock + private MetricsLogger mMetricsLogger; + private UiEventLoggerFake mUiEventLogger = new UiEventLoggerFake(); + private DumpManager mDumpManager = new DumpManager(); + @Mock + QSTileImpl mQSTile; + @Mock + QSTileView mQSTileView; + + private QSPanelControllerBase<QSPanel> mController; + + /** Implementation needed to ensure we have a reflectively-available class name. */ + private static class TestableQSPanelControllerBase extends QSPanelControllerBase<QSPanel> { + protected TestableQSPanelControllerBase(QSPanel view, QSTileHost host, + MetricsLogger metricsLogger, + UiEventLogger uiEventLogger, DumpManager dumpManager) { + super(view, host, metricsLogger, uiEventLogger, dumpManager); + } + } + + @Before + public void setup() throws Exception { + MockitoAnnotations.initMocks(this); + + when(mQSPanel.getMediaHost()).thenReturn(mMediaHost); + when(mQSPanel.isAttachedToWindow()).thenReturn(true); + when(mQSPanel.getDumpableTag()).thenReturn("QSPanel"); + when(mQSPanel.openPanelEvent()).thenReturn(QSEvent.QS_PANEL_EXPANDED); + when(mQSPanel.closePanelEvent()).thenReturn(QSEvent.QS_PANEL_COLLAPSED); + when(mQSTileHost.getTiles()).thenReturn(Collections.singleton(mQSTile)); + when(mQSTileHost.createTileView(eq(mQSTile), anyBoolean())).thenReturn(mQSTileView); + + mController = new TestableQSPanelControllerBase(mQSPanel, mQSTileHost, + mMetricsLogger, mUiEventLogger, mDumpManager); + + mController.init(); + } + + @Test + public void testSetExpanded_Metrics() { + mController.setExpanded(true); + verify(mMetricsLogger).visibility(eq(MetricsEvent.QS_PANEL), eq(true)); + assertEquals(1, mUiEventLogger.numLogs()); + assertEquals(QSEvent.QS_PANEL_EXPANDED.getId(), mUiEventLogger.eventId(0)); + mUiEventLogger.getLogs().clear(); + + mController.setExpanded(false); + verify(mMetricsLogger).visibility(eq(MetricsEvent.QS_PANEL), eq(false)); + assertEquals(1, mUiEventLogger.numLogs()); + assertEquals(QSEvent.QS_PANEL_COLLAPSED.getId(), mUiEventLogger.eventId(0)); + mUiEventLogger.getLogs().clear(); + + } + + @Test + public void testDump() { + String mockTileViewString = "Mock Tile View"; + String mockTileString = "Mock Tile"; + doAnswer(invocation -> { + PrintWriter pw = invocation.getArgument(1); + pw.println(mockTileString); + return null; + }).when(mQSTile).dump(any(FileDescriptor.class), any(PrintWriter.class), + any(String[].class)); + when(mQSTileView.toString()).thenReturn(mockTileViewString); + + StringWriter w = new StringWriter(); + PrintWriter pw = new PrintWriter(w); + mController.dump(mock(FileDescriptor.class), pw, new String[]{}); + String expected = "TestableQSPanelControllerBase:\n" + + " Tile records:\n" + + " " + mockTileString + "\n" + + " " + mockTileViewString + "\n"; + assertEquals(expected, w.getBuffer().toString()); + } + +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.java new file mode 100644 index 000000000000..0ba0214d2173 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.java @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper.RunWithLooper; + +import androidx.test.filters.SmallTest; + +import com.android.internal.logging.MetricsLogger; +import com.android.internal.logging.UiEventLogger; +import com.android.internal.logging.testing.UiEventLoggerFake; +import com.android.systemui.SysuiTestCase; +import com.android.systemui.dump.DumpManager; +import com.android.systemui.media.MediaHost; +import com.android.systemui.plugins.qs.QSTileView; +import com.android.systemui.qs.tileimpl.QSTileImpl; +import com.android.systemui.settings.BrightnessController; +import com.android.systemui.settings.ToggleSlider; +import com.android.systemui.tuner.TunerService; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.util.Collections; + +@RunWith(AndroidTestingRunner.class) +@RunWithLooper +@SmallTest +public class QSPanelControllerTest extends SysuiTestCase { + + @Mock + private QSPanel mQSPanel; + @Mock + private QSTileHost mQSTileHost; + @Mock + private MediaHost mMediaHost; + @Mock + private MetricsLogger mMetricsLogger; + private UiEventLogger mUiEventLogger = new UiEventLoggerFake(); + private DumpManager mDumpManager = new DumpManager(); + @Mock + private TunerService mTunerService; + @Mock + private QSSecurityFooter mQSSecurityFooter; + @Mock + private BrightnessController.Factory mBrightnessControllerFactory; + @Mock + private BrightnessController mBrightnessController; + @Mock + QSTileImpl mQSTile; + @Mock + QSTileView mQSTileView; + + private QSPanelController mController; + + @Before + public void setup() throws Exception { + MockitoAnnotations.initMocks(this); + + when(mQSPanel.getMediaHost()).thenReturn(mMediaHost); + when(mQSPanel.isAttachedToWindow()).thenReturn(true); + when(mQSPanel.getDumpableTag()).thenReturn("QSPanel"); + when(mQSTileHost.getTiles()).thenReturn(Collections.singleton(mQSTile)); + when(mQSTileHost.createTileView(eq(mQSTile), anyBoolean())).thenReturn(mQSTileView); + when(mBrightnessControllerFactory.create(any(ToggleSlider.class))) + .thenReturn(mBrightnessController); + + mController = new QSPanelController(mQSPanel, mQSSecurityFooter, mTunerService, + mQSTileHost, mDumpManager, mMetricsLogger, mUiEventLogger, + mBrightnessControllerFactory); + + mController.init(); + } + + @Test + public void testOpenDetailsWithNonExistingTile_NoException() { + mController.openDetails("none"); + + verify(mQSPanel, never()).openDetails(any()); + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.java index 4b7a26870308..e38d54b90f69 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.java @@ -14,12 +14,9 @@ package com.android.systemui.qs; -import static org.junit.Assert.assertEquals; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; @@ -36,19 +33,15 @@ import android.widget.FrameLayout; import androidx.test.filters.SmallTest; import com.android.internal.logging.MetricsLogger; -import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.logging.testing.UiEventLoggerFake; import com.android.systemui.Dependency; import com.android.systemui.SysuiTestCase; -import com.android.systemui.broadcast.BroadcastDispatcher; -import com.android.systemui.dump.DumpManager; import com.android.systemui.media.MediaHost; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.qs.QSTileView; import com.android.systemui.qs.customize.QSCustomizer; import com.android.systemui.qs.logging.QSLogger; import com.android.systemui.qs.tileimpl.QSTileImpl; -import com.android.systemui.settings.UserTracker; import com.android.systemui.statusbar.policy.SecurityController; import com.android.systemui.util.animation.DisappearParameters; import com.android.systemui.util.animation.UniqueObjectHostView; @@ -59,9 +52,6 @@ import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; -import java.io.FileDescriptor; -import java.io.PrintWriter; -import java.io.StringWriter; import java.util.Collections; @RunWith(AndroidTestingRunner.class) @@ -79,9 +69,9 @@ public class QSPanelTest extends SysuiTestCase { @Mock private QSTileImpl dndTile; @Mock - private BroadcastDispatcher mBroadcastDispatcher; + private QSTileImpl mNonTile; @Mock - private DumpManager mDumpManager; + private QSPanelControllerBase.TileRecord mDndTileRecord; @Mock private QSLogger mQSLogger; private ViewGroup mParentView; @@ -93,9 +83,8 @@ public class QSPanelTest extends SysuiTestCase { private MediaHost mMediaHost; @Mock private ActivityStarter mActivityStarter; - @Mock(stubOnly = true) - private UserTracker mUserTracker; private UiEventLoggerFake mUiEventLogger; + private String mCachedSpecs = ""; @Before public void setup() throws Exception { @@ -109,12 +98,13 @@ public class QSPanelTest extends SysuiTestCase { mContext.addMockSystemService(Context.USER_SERVICE, mock(UserManager.class)); when(mMediaHost.getHostView()).thenReturn(new UniqueObjectHostView(getContext())); when(mMediaHost.getDisappearParameters()).thenReturn(new DisappearParameters()); + mDndTileRecord.tile = dndTile; + mDndTileRecord.tileView = mQSTileView; mUiEventLogger = new UiEventLoggerFake(); mTestableLooper.runWithLooper(() -> { mMetricsLogger = mDependency.injectMockDependency(MetricsLogger.class); - mQsPanel = new QSPanel(mContext, null, mDumpManager, mBroadcastDispatcher, - mQSLogger, mMediaHost, mUiEventLogger, mUserTracker); + mQsPanel = new QSPanel(mContext, null, mQSLogger, mMediaHost, mUiEventLogger); mQsPanel.onFinishInflate(); // Provides a parent with non-zero size for QSPanel mParentView = new FrameLayout(mContext); @@ -124,8 +114,8 @@ public class QSPanelTest extends SysuiTestCase { when(mHost.getTiles()).thenReturn(Collections.emptyList()); when(mHost.createTileView(any(), anyBoolean())).thenReturn(mQSTileView); - mQsPanel.setHost(mHost, mCustomizer); - mQsPanel.addTile(dndTile, true); + mQsPanel.setCustomizer(mCustomizer); + mQsPanel.addTile(mDndTileRecord); mQsPanel.setCallback(mCallback); }); } @@ -133,25 +123,16 @@ public class QSPanelTest extends SysuiTestCase { @Test public void testSetExpanded_Metrics() { mQsPanel.setExpanded(true); - verify(mMetricsLogger).visibility(eq(MetricsEvent.QS_PANEL), eq(true)); verify(mQSLogger).logPanelExpanded(true, mQsPanel.getDumpableTag()); - assertEquals(1, mUiEventLogger.numLogs()); - assertEquals(QSEvent.QS_PANEL_EXPANDED.getId(), mUiEventLogger.eventId(0)); - mUiEventLogger.getLogs().clear(); mQsPanel.setExpanded(false); - verify(mMetricsLogger).visibility(eq(MetricsEvent.QS_PANEL), eq(false)); verify(mQSLogger).logPanelExpanded(false, mQsPanel.getDumpableTag()); - assertEquals(1, mUiEventLogger.numLogs()); - assertEquals(QSEvent.QS_PANEL_COLLAPSED.getId(), mUiEventLogger.eventId(0)); - mUiEventLogger.getLogs().clear(); - } @Test public void testOpenDetailsWithExistingTile_NoException() { mTestableLooper.processAllMessages(); - mQsPanel.openDetails("dnd"); + mQsPanel.openDetails(dndTile); mTestableLooper.processAllMessages(); verify(mCallback).onShowingDetail(any(), anyInt(), anyInt()); @@ -159,53 +140,19 @@ public class QSPanelTest extends SysuiTestCase { @Test public void setListening() { - when(dndTile.getTileSpec()).thenReturn("dnd"); - - mQsPanel.setListening(true); + mQsPanel.setListening(true, "dnd"); verify(mQSLogger).logAllTilesChangeListening(true, mQsPanel.getDumpableTag(), "dnd"); - mQsPanel.setListening(false); + mQsPanel.setListening(false, "dnd"); verify(mQSLogger).logAllTilesChangeListening(false, mQsPanel.getDumpableTag(), "dnd"); } -/* @Test + @Test public void testOpenDetailsWithNullParameter_NoException() { mTestableLooper.processAllMessages(); mQsPanel.openDetails(null); mTestableLooper.processAllMessages(); verify(mCallback, never()).onShowingDetail(any(), anyInt(), anyInt()); - }*/ - - @Test - public void testOpenDetailsWithNonExistingTile_NoException() { - mTestableLooper.processAllMessages(); - mQsPanel.openDetails("invalid-name"); - mTestableLooper.processAllMessages(); - - verify(mCallback, never()).onShowingDetail(any(), anyInt(), anyInt()); - } - - @Test - public void testDump() { - String mockTileViewString = "Mock Tile View"; - String mockTileString = "Mock Tile"; - doAnswer(invocation -> { - PrintWriter pw = invocation.getArgument(1); - pw.println(mockTileString); - return null; - }).when(dndTile).dump(any(FileDescriptor.class), any(PrintWriter.class), - any(String[].class)); - when(mQSTileView.toString()).thenReturn(mockTileViewString); - - StringWriter w = new StringWriter(); - PrintWriter pw = new PrintWriter(w); - mQsPanel.dump(mock(FileDescriptor.class), pw, new String[]{}); - String expected = "QSPanel:\n" - + " Tile records:\n" - + " " + mockTileString + "\n" - + " " + mockTileViewString + "\n"; - assertEquals(expected, w.getBuffer().toString()); } - } diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/TileLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/TileLayoutTest.java index fef47bd6a392..6c7c20a15140 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/TileLayoutTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/TileLayoutTest.java @@ -57,8 +57,8 @@ public class TileLayoutTest extends SysuiTestCase { mContext.getResources().getDimensionPixelSize(R.dimen.qs_tile_margin_horizontal) * 3; } - private QSPanel.TileRecord createTileRecord() { - QSPanel.TileRecord tileRecord = new QSPanel.TileRecord(); + private QSPanelControllerBase.TileRecord createTileRecord() { + QSPanelControllerBase.TileRecord tileRecord = new QSPanelControllerBase.TileRecord(); tileRecord.tile = mock(QSTile.class); tileRecord.tileView = spy(new QSTileView(mContext, new QSIconViewImpl(mContext))); return tileRecord; @@ -66,14 +66,14 @@ public class TileLayoutTest extends SysuiTestCase { @Test public void testAddTile_CallsSetListeningOnTile() { - QSPanel.TileRecord tileRecord = createTileRecord(); + QSPanelControllerBase.TileRecord tileRecord = createTileRecord(); mTileLayout.addTile(tileRecord); verify(tileRecord.tile, times(1)).setListening(mTileLayout, false); } @Test public void testSetListening_CallsSetListeningOnTile() { - QSPanel.TileRecord tileRecord = createTileRecord(); + QSPanelControllerBase.TileRecord tileRecord = createTileRecord(); mTileLayout.addTile(tileRecord); mTileLayout.setListening(true); verify(tileRecord.tile, times(1)).setListening(mTileLayout, true); @@ -81,7 +81,7 @@ public class TileLayoutTest extends SysuiTestCase { @Test public void testSetListening_SameValueIsNoOp() { - QSPanel.TileRecord tileRecord = createTileRecord(); + QSPanelControllerBase.TileRecord tileRecord = createTileRecord(); mTileLayout.addTile(tileRecord); mTileLayout.setListening(false); verify(tileRecord.tile, times(1)).setListening(any(), anyBoolean()); @@ -89,7 +89,7 @@ public class TileLayoutTest extends SysuiTestCase { @Test public void testSetListening_ChangesValueForAddingFutureTiles() { - QSPanel.TileRecord tileRecord = createTileRecord(); + QSPanelControllerBase.TileRecord tileRecord = createTileRecord(); mTileLayout.setListening(true); mTileLayout.addTile(tileRecord); verify(tileRecord.tile, times(1)).setListening(mTileLayout, true); @@ -97,7 +97,7 @@ public class TileLayoutTest extends SysuiTestCase { @Test public void testRemoveTile_CallsSetListeningFalseOnTile() { - QSPanel.TileRecord tileRecord = createTileRecord(); + QSPanelControllerBase.TileRecord tileRecord = createTileRecord(); mTileLayout.setListening(true); mTileLayout.addTile(tileRecord); mTileLayout.removeTile(tileRecord); @@ -106,8 +106,8 @@ public class TileLayoutTest extends SysuiTestCase { @Test public void testRemoveAllViews_CallsSetListeningFalseOnAllTiles() { - QSPanel.TileRecord tileRecord1 = createTileRecord(); - QSPanel.TileRecord tileRecord2 = createTileRecord(); + QSPanelControllerBase.TileRecord tileRecord1 = createTileRecord(); + QSPanelControllerBase.TileRecord tileRecord2 = createTileRecord(); mTileLayout.setListening(true); mTileLayout.addTile(tileRecord1); mTileLayout.addTile(tileRecord2); @@ -118,7 +118,7 @@ public class TileLayoutTest extends SysuiTestCase { @Test public void testMeasureLayout_CallsLayoutOnTile() { - QSPanel.TileRecord tileRecord = createTileRecord(); + QSPanelControllerBase.TileRecord tileRecord = createTileRecord(); mTileLayout.addTile(tileRecord); mTileLayout.measure(mLayoutSizeForOneTile, mLayoutSizeForOneTile); mTileLayout.layout(0, 0, mLayoutSizeForOneTile, mLayoutSizeForOneTile); @@ -127,8 +127,8 @@ public class TileLayoutTest extends SysuiTestCase { @Test public void testMeasureLayout_CallsLayoutOnTilesWithNeighboredBounds() { - QSPanel.TileRecord tileRecord1 = createTileRecord(); - QSPanel.TileRecord tileRecord2 = createTileRecord(); + QSPanelControllerBase.TileRecord tileRecord1 = createTileRecord(); + QSPanelControllerBase.TileRecord tileRecord2 = createTileRecord(); mTileLayout.addTile(tileRecord1); mTileLayout.addTile(tileRecord2); mTileLayout.measure(mLayoutSizeForOneTile * 2, mLayoutSizeForOneTile * 2); diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureTest.java index e7ef64e6adad..2b3ca7c00a49 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureTest.java @@ -25,8 +25,8 @@ import android.os.RemoteException; import android.testing.AndroidTestingRunner; import android.util.Log; import android.view.Display; -import android.view.IScrollCaptureClient; -import android.view.IScrollCaptureController; +import android.view.IScrollCaptureCallbacks; +import android.view.IScrollCaptureConnection; import android.view.IWindowManager; import android.view.WindowManagerGlobal; @@ -65,19 +65,20 @@ public class ScrollCaptureTest extends SysuiTestCase { final CountDownLatch latch = new CountDownLatch(1); try { wms.requestScrollCapture(Display.DEFAULT_DISPLAY, null, -1, - new IScrollCaptureController.Stub() { + new IScrollCaptureCallbacks.Stub() { @Override - public void onClientConnected( - IScrollCaptureClient client, Rect scrollBounds, + public void onConnected( + IScrollCaptureConnection connection, Rect scrollBounds, Point positionInWindow) { Log.d(TAG, - "client connected: " + client + "[scrollBounds= " + scrollBounds - + ", positionInWindow=" + positionInWindow + "]"); + "client connected: " + connection + "[scrollBounds= " + + scrollBounds + ", " + + "positionInWindow=" + positionInWindow + "]"); latch.countDown(); } @Override - public void onClientUnavailable() { + public void onUnavailable() { } @Override diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java index d04d8ee76b99..0be9f7de5825 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java @@ -380,6 +380,36 @@ public class NotificationEntryManagerTest extends SysuiTestCase { } @Test + public void testUpdatePendingNotification_rankingUpdated() { + // GIVEN a notification with ranking is pending + final Ranking originalRanking = mEntry.getRanking(); + mEntryManager.mPendingNotifications.put(mEntry.getKey(), mEntry); + + // WHEN the same notification has been updated with a new ranking + final int newRank = 2345; + doAnswer(invocationOnMock -> { + Ranking ranking = (Ranking) + invocationOnMock.getArguments()[1]; + ranking.populate( + mEntry.getKey(), + newRank, /* this changed!! */ + false, + 0, + 0, + IMPORTANCE_DEFAULT, + null, null, + null, null, null, true, + Ranking.USER_SENTIMENT_NEUTRAL, false, -1, + false, null, null, false, false, false, null, 0, false); + return true; + }).when(mRankingMap).getRanking(eq(mEntry.getKey()), any(Ranking.class)); + mEntryManager.addNotification(mSbn, mRankingMap); + + // THEN ranking for the entry has been updated with new ranking + assertEquals(newRank, mEntry.getRanking().getRank()); + } + + @Test public void testLifetimeExtenders_ifNotificationIsRetainedItIsntRemoved() { // GIVEN an entry manager with a notification mEntryManager.addActiveNotificationForTest(mEntry); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java index 8f1d71c80e2d..891179c32315 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java @@ -96,8 +96,8 @@ public class ExpandableNotificationRowTest extends SysuiTestCase { mGroupRow.setSensitive(true, true); mGroupRow.setHideSensitive(true, false, 0, 0); mGroupRow.setHideSensitive(false, true, 0, 0); - assertTrue(mGroupRow.getChildrenContainer().getVisibleHeader().getVisibility() - == View.VISIBLE); + assertEquals(View.VISIBLE, mGroupRow.getChildrenContainer().getVisibleWrapper() + .getNotificationHeader().getVisibility()); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerTest.java index b9055ec0bb7b..7c41abba6176 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerTest.java @@ -57,7 +57,7 @@ public class NotificationChildrenContainerTest extends SysuiTestCase { public void testGetMaxAllowedVisibleChildren_lowPriority() { mChildrenContainer.setIsLowPriority(true); Assert.assertEquals(mChildrenContainer.getMaxAllowedVisibleChildren(), - NotificationChildrenContainer.NUMBER_OF_CHILDREN_WHEN_SYSTEM_EXPANDED); + NotificationChildrenContainer.NUMBER_OF_CHILDREN_WHEN_SYSTEM_EXPANDED); } @Test @@ -72,7 +72,7 @@ public class NotificationChildrenContainerTest extends SysuiTestCase { mChildrenContainer.setIsLowPriority(true); mChildrenContainer.setChildrenExpanded(true); Assert.assertEquals(mChildrenContainer.getMaxAllowedVisibleChildren(), - NotificationChildrenContainer.NUMBER_OF_CHILDREN_WHEN_SYSTEM_EXPANDED); + NotificationChildrenContainer.NUMBER_OF_CHILDREN_WHEN_SYSTEM_EXPANDED); } @Test @@ -80,13 +80,13 @@ public class NotificationChildrenContainerTest extends SysuiTestCase { mChildrenContainer.setIsLowPriority(true); mChildrenContainer.setUserLocked(true); Assert.assertEquals(mChildrenContainer.getMaxAllowedVisibleChildren(), - NotificationChildrenContainer.NUMBER_OF_CHILDREN_WHEN_SYSTEM_EXPANDED); + NotificationChildrenContainer.NUMBER_OF_CHILDREN_WHEN_SYSTEM_EXPANDED); } @Test public void testGetMaxAllowedVisibleChildren_likeCollapsed() { Assert.assertEquals(mChildrenContainer.getMaxAllowedVisibleChildren(true), - NotificationChildrenContainer.NUMBER_OF_CHILDREN_WHEN_COLLAPSED); + NotificationChildrenContainer.NUMBER_OF_CHILDREN_WHEN_COLLAPSED); } @@ -136,12 +136,13 @@ public class NotificationChildrenContainerTest extends SysuiTestCase { @Test public void testLowPriorityHeaderCleared() { mGroup.setIsLowPriority(true); - NotificationHeaderView lowPriorityHeaderView = mChildrenContainer.getLowPriorityHeaderView(); - Assert.assertTrue(lowPriorityHeaderView.getVisibility() == View.VISIBLE); - Assert.assertTrue(lowPriorityHeaderView.getParent() == mChildrenContainer); + NotificationHeaderView lowPriorityHeaderView = + mChildrenContainer.getLowPriorityViewWrapper().getNotificationHeader(); + Assert.assertEquals(View.VISIBLE, lowPriorityHeaderView.getVisibility()); + Assert.assertSame(mChildrenContainer, lowPriorityHeaderView.getParent()); mGroup.setIsLowPriority(false); - Assert.assertTrue(lowPriorityHeaderView.getParent() == null); - Assert.assertTrue(mChildrenContainer.getLowPriorityHeaderView() == null); + Assert.assertNull(lowPriorityHeaderView.getParent()); + Assert.assertNull(mChildrenContainer.getLowPriorityViewWrapper()); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java index 386844317720..cb56f1fbc054 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java @@ -53,6 +53,7 @@ import com.android.internal.util.LatencyTracker; import com.android.keyguard.KeyguardClockSwitch; import com.android.keyguard.KeyguardClockSwitchController; import com.android.keyguard.KeyguardStatusView; +import com.android.keyguard.KeyguardStatusViewController; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.dagger.KeyguardStatusViewComponent; import com.android.systemui.R; @@ -190,6 +191,8 @@ public class NotificationPanelViewTest extends SysuiTestCase { @Mock private KeyguardClockSwitchController mKeyguardClockSwitchController; @Mock + private KeyguardStatusViewController mKeyguardStatusViewController; + @Mock private NotificationStackScrollLayoutController mNotificationStackScrollLayoutController; private NotificationPanelViewController mNotificationPanelViewController; @@ -246,6 +249,8 @@ public class NotificationPanelViewTest extends SysuiTestCase { .thenReturn(mKeyguardStatusViewComponent); when(mKeyguardStatusViewComponent.getKeyguardClockSwitchController()) .thenReturn(mKeyguardClockSwitchController); + when(mKeyguardStatusViewComponent.getKeyguardStatusViewController()) + .thenReturn(mKeyguardStatusViewController); mNotificationPanelViewController = new NotificationPanelViewController(mView, mResources, mInjectionInflationController, diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java index a6ea9966a0f1..d6a7acbcbd78 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java @@ -24,6 +24,7 @@ import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertTrue; import static junit.framework.TestCase.fail; +import static org.junit.Assert.assertEquals; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; @@ -34,6 +35,7 @@ import static org.mockito.Mockito.doAnswer; 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; @@ -43,6 +45,7 @@ import android.app.StatusBarManager; import android.app.trust.TrustManager; import android.content.BroadcastReceiver; import android.content.ContentResolver; +import android.content.Intent; import android.content.IntentFilter; import android.hardware.display.AmbientDisplayConfiguration; import android.hardware.fingerprint.FingerprintManager; @@ -84,6 +87,7 @@ import com.android.systemui.bubbles.Bubbles; import com.android.systemui.classifier.FalsingManagerFake; import com.android.systemui.colorextraction.SysuiColorExtractor; import com.android.systemui.demomode.DemoModeController; +import com.android.systemui.emergency.EmergencyGesture; import com.android.systemui.keyguard.DismissCallbackRegistry; import com.android.systemui.keyguard.KeyguardViewMediator; import com.android.systemui.keyguard.ScreenLifecycle; @@ -146,6 +150,7 @@ import com.android.wm.shell.splitscreen.SplitScreen; 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; @@ -873,6 +878,19 @@ public class StatusBarTest extends SysuiTestCase { verify(mDozeServiceHost).setDozeSuppressed(false); } + @Test + public void onEmergencyActionLaunchGesture_launchesEmergencyIntent() { + ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class); + StatusBar statusBarSpy = spy(mStatusBar); + + statusBarSpy.onEmergencyActionLaunchGestureDetected(); + + verify(statusBarSpy).startActivity(intentCaptor.capture(), eq(true)); + Intent sentIntent = intentCaptor.getValue(); + assertEquals(sentIntent.getAction(), EmergencyGesture.ACTION_LAUNCH_EMERGENCY); + + } + public static class TestableNotificationInterruptStateProviderImpl extends NotificationInterruptStateProviderImpl { diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeTunerService.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeTunerService.java index 8db82e2a52fc..97d4aa7e4d19 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeTunerService.java +++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeTunerService.java @@ -14,7 +14,7 @@ package com.android.systemui.utils.leaks; -import android.content.Context; +import android.os.UserHandle; import android.testing.LeakCheck; import com.android.systemui.tuner.TunerService; @@ -22,8 +22,10 @@ import com.android.systemui.tuner.TunerService; public class FakeTunerService extends TunerService { private final BaseLeakChecker<Tunable> mBaseLeakChecker; + private boolean mEnabled; public FakeTunerService(LeakCheck test) { + super(null); mBaseLeakChecker = new BaseLeakChecker<>(test, "tunable"); } @@ -74,4 +76,14 @@ public class FakeTunerService extends TunerService { public void setValue(String setting, int value) { } + + @Override + public void setTunerEnabled(UserHandle user, boolean enabled) { + mEnabled = enabled; + } + + @Override + public boolean isTunerEnabled(UserHandle user) { + return mEnabled; + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java index 2bc07ed43e29..dd7f263f124a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java @@ -16,22 +16,18 @@ package com.android.systemui.wmshell; -import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE; - import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.content.pm.PackageManager; import android.test.suitebuilder.annotation.SmallTest; -import android.testing.TestableContext; import androidx.test.runner.AndroidJUnit4; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.KeyguardUpdateMonitorCallback; +import com.android.systemui.SystemUIFactory; import com.android.systemui.SysuiTestCase; import com.android.systemui.keyguard.ScreenLifecycle; import com.android.systemui.model.SysUiState; @@ -58,6 +54,7 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; import java.util.Optional; +import java.util.concurrent.ExecutionException; @SmallTest @RunWith(AndroidJUnit4.class) @@ -69,7 +66,6 @@ public class WMShellTest extends SysuiTestCase { @Mock ConfigurationController mConfigurationController; @Mock KeyguardUpdateMonitor mKeyguardUpdateMonitor; @Mock TaskStackChangeListeners mTaskStackChangeListeners; - @Mock DisplayImeController mDisplayImeController; @Mock InputConsumerController mMockInputConsumerController; @Mock NavigationModeController mNavigationModeController; @Mock ScreenLifecycle mScreenLifecycle; @@ -89,19 +85,10 @@ public class WMShellTest extends SysuiTestCase { mWMShell = new WMShell(mContext, mCommandQueue, mConfigurationController, mInputConsumerController, mKeyguardUpdateMonitor, mTaskStackChangeListeners, - mDisplayImeController, mNavigationModeController, mScreenLifecycle, mSysUiState, - Optional.of(mPip), Optional.of(mSplitScreen), Optional.of(mOneHanded), - mTaskOrganizer, mProtoTracer); + mNavigationModeController, mScreenLifecycle, mSysUiState, Optional.of(mPip), + Optional.of(mSplitScreen), Optional.of(mOneHanded), mTaskOrganizer, mProtoTracer); when(mPip.getPipTouchHandler()).thenReturn(mPipTouchHandler); - - } - - @Test - public void start_startsMonitorDisplays() { - mWMShell.start(); - - verify(mDisplayImeController).startMonitorDisplays(); } @Test @@ -112,26 +99,6 @@ public class WMShellTest extends SysuiTestCase { } @Test - public void nonPipDevice_shouldNotInitPip() { - final TestableContext nonPipContext = getNonPipFeatureContext(); - final WMShell nonPipWMShell = new WMShell(nonPipContext, mCommandQueue, - mConfigurationController, mMockInputConsumerController, mKeyguardUpdateMonitor, - mTaskStackChangeListeners, mDisplayImeController, mNavigationModeController, - mScreenLifecycle, mSysUiState, Optional.of(mPip), Optional.of(mSplitScreen), - Optional.of(mOneHanded), mTaskOrganizer, mProtoTracer); - nonPipWMShell.initPip(mPip); - - verify(mCommandQueue, never()).addCallback(any()); - verify(mKeyguardUpdateMonitor, never()).registerCallback(any()); - verify(mConfigurationController, never()).addCallback(any()); - verify(mSysUiState, never()).addCallback(any()); - verify(mTaskStackChangeListeners, never()).registerTaskStackListener(any()); - verify(mMockInputConsumerController, never()).setInputListener(any()); - verify(mMockInputConsumerController, never()).setRegistrationListener(any()); - verify(mPip, never()).registerSessionListenerForCurrentUser(); - } - - @Test public void initSplitScreen_registersCallbacks() { mWMShell.initSplitScreen(mSplitScreen); @@ -156,11 +123,4 @@ public class WMShellTest extends SysuiTestCase { OneHandedGestureHandler.OneHandedGestureEventCallback.class)); verify(mOneHanded).registerTransitionCallback(any(OneHandedTransitionCallback.class)); } - - TestableContext getNonPipFeatureContext() { - TestableContext spiedContext = spy(mContext); - when(mMockPackageManager.hasSystemFeature(FEATURE_PICTURE_IN_PICTURE)).thenReturn(false); - when(spiedContext.getPackageManager()).thenReturn(mMockPackageManager); - return spiedContext; - } } diff --git a/packages/Tethering/common/TetheringLib/src/android/net/TetheredClient.java b/packages/Tethering/common/TetheringLib/src/android/net/TetheredClient.java index 645b00001330..0b223f42b954 100644 --- a/packages/Tethering/common/TetheringLib/src/android/net/TetheredClient.java +++ b/packages/Tethering/common/TetheringLib/src/android/net/TetheredClient.java @@ -19,7 +19,6 @@ package android.net; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.os.Parcel; import android.os.Parcelable; @@ -34,7 +33,6 @@ import java.util.Objects; * @hide */ @SystemApi -@TestApi public final class TetheredClient implements Parcelable { @NonNull private final MacAddress mMacAddress; diff --git a/packages/Tethering/common/TetheringLib/src/android/net/TetheringManager.java b/packages/Tethering/common/TetheringLib/src/android/net/TetheringManager.java index db8436859281..13b05a841d24 100644 --- a/packages/Tethering/common/TetheringLib/src/android/net/TetheringManager.java +++ b/packages/Tethering/common/TetheringLib/src/android/net/TetheringManager.java @@ -23,7 +23,6 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.content.Context; import android.os.Bundle; import android.os.ConditionVariable; @@ -55,7 +54,6 @@ import java.util.function.Supplier; * @hide */ @SystemApi -@TestApi public class TetheringManager { private static final String TAG = TetheringManager.class.getSimpleName(); private static final int DEFAULT_TIMEOUT_MS = 60_000; diff --git a/packages/Tethering/src/com/android/networkstack/tethering/PrivateAddressCoordinator.java b/packages/Tethering/src/com/android/networkstack/tethering/PrivateAddressCoordinator.java index 0cf14e3f868c..4f616cdff086 100644 --- a/packages/Tethering/src/com/android/networkstack/tethering/PrivateAddressCoordinator.java +++ b/packages/Tethering/src/com/android/networkstack/tethering/PrivateAddressCoordinator.java @@ -36,6 +36,7 @@ import android.util.ArrayMap; import android.util.ArraySet; import android.util.SparseArray; +import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.android.internal.annotations.VisibleForTesting; @@ -79,11 +80,6 @@ public class PrivateAddressCoordinator { private final SparseArray<LinkAddress> mCachedAddresses; public PrivateAddressCoordinator(Context context, TetheringConfiguration config) { - this(context, config, new ArrayList<>(Arrays.asList(new IpPrefix("192.168.0.0/16")))); - } - - public PrivateAddressCoordinator(Context context, TetheringConfiguration config, - List<IpPrefix> prefixPools) { mDownstreams = new ArraySet<>(); mUpstreamPrefixMap = new ArrayMap<>(); mConnectivityMgr = (ConnectivityManager) context.getSystemService( @@ -94,7 +90,11 @@ public class PrivateAddressCoordinator { mCachedAddresses.put(TETHERING_BLUETOOTH, new LinkAddress(LEGACY_BLUETOOTH_IFACE_ADDRESS)); mCachedAddresses.put(TETHERING_WIFI_P2P, new LinkAddress(LEGACY_WIFI_P2P_IFACE_ADDRESS)); - mTetheringPrefixes = prefixPools; + mTetheringPrefixes = new ArrayList<>(Arrays.asList(new IpPrefix("192.168.0.0/16"))); + if (config.isSelectAllPrefixRangeEnabled()) { + mTetheringPrefixes.add(new IpPrefix("172.16.0.0/12")); + mTetheringPrefixes.add(new IpPrefix("10.0.0.0/8")); + } } /** @@ -135,7 +135,6 @@ public class PrivateAddressCoordinator { private void handleMaybePrefixConflict(final List<IpPrefix> prefixes) { for (IpServer downstream : mDownstreams) { final IpPrefix target = getDownstreamPrefix(downstream); - if (target == null) continue; for (IpPrefix source : prefixes) { if (isConflictPrefix(source, target)) { @@ -179,6 +178,7 @@ public class PrivateAddressCoordinator { final LinkAddress cachedAddress = mCachedAddresses.get(ipServer.interfaceType()); if (useLastAddress && cachedAddress != null && !isConflictWithUpstream(asIpPrefix(cachedAddress))) { + mDownstreams.add(ipServer); return cachedAddress; } @@ -370,7 +370,6 @@ public class PrivateAddressCoordinator { // in mCachedAddresses. for (IpServer downstream : mDownstreams) { final IpPrefix target = getDownstreamPrefix(downstream); - if (target == null) continue; if (isConflictPrefix(prefix, target)) return target; } @@ -378,9 +377,9 @@ public class PrivateAddressCoordinator { return null; } + @NonNull private IpPrefix getDownstreamPrefix(final IpServer downstream) { final LinkAddress address = downstream.getAddress(); - if (address == null) return null; return asIpPrefix(address); } diff --git a/packages/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java b/packages/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java index 5783805861a3..799637c9b1c5 100644 --- a/packages/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java +++ b/packages/Tethering/src/com/android/networkstack/tethering/TetheringConfiguration.java @@ -40,7 +40,6 @@ import java.util.Arrays; import java.util.Collection; import java.util.StringJoiner; - /** * A utility class to encapsulate the various tethering configuration elements. * @@ -88,6 +87,13 @@ public class TetheringConfiguration { "use_legacy_wifi_p2p_dedicated_ip"; /** + * Flag use to enable select all prefix ranges feature. + * TODO: Remove this flag if there are no problems after M-2020-12 rolls out. + */ + public static final String TETHER_ENABLE_SELECT_ALL_PREFIX_RANGES = + "tether_enable_select_all_prefix_ranges"; + + /** * Default value that used to periodic polls tether offload stats from tethering offload HAL * to make the data warnings work. */ @@ -118,6 +124,8 @@ public class TetheringConfiguration { private final boolean mEnableBpfOffload; private final boolean mEnableWifiP2pDedicatedIp; + private final boolean mEnableSelectAllPrefixRange; + public TetheringConfiguration(Context ctx, SharedLog log, int id) { final SharedLog configLog = log.forSubComponent("config"); @@ -164,6 +172,11 @@ public class TetheringConfiguration { R.bool.config_tether_enable_legacy_wifi_p2p_dedicated_ip, false /* defaultValue */); + // Flags should normally not be booleans, but this is a kill-switch flag that is only used + // to turn off the feature, so binary rollback problems do not apply. + mEnableSelectAllPrefixRange = getDeviceConfigBoolean( + TETHER_ENABLE_SELECT_ALL_PREFIX_RANGES, true /* defaultValue */); + configLog.log(toString()); } @@ -249,6 +262,9 @@ public class TetheringConfiguration { pw.print("enableWifiP2pDedicatedIp: "); pw.println(mEnableWifiP2pDedicatedIp); + + pw.print("mEnableSelectAllPrefixRange: "); + pw.println(mEnableSelectAllPrefixRange); } /** Returns the string representation of this object.*/ @@ -310,6 +326,10 @@ public class TetheringConfiguration { return mEnableBpfOffload; } + public boolean isSelectAllPrefixRangeEnabled() { + return mEnableSelectAllPrefixRange; + } + private static Collection<Integer> getUpstreamIfaceTypes(Resources res, boolean dunRequired) { final int[] ifaceTypes = res.getIntArray(R.array.config_tether_upstream_types); final ArrayList<Integer> upstreamIfaceTypes = new ArrayList<>(ifaceTypes.length); diff --git a/packages/Tethering/tests/mts/Android.bp b/packages/Tethering/tests/mts/Android.bp new file mode 100644 index 000000000000..f925b0a53f32 --- /dev/null +++ b/packages/Tethering/tests/mts/Android.bp @@ -0,0 +1,56 @@ +// Copyright (C) 2020 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +android_test { + // This tests for functionality that is not required for devices that + // don't use Tethering mainline module. + name: "MtsTetheringTest", + + libs: [ + "android.test.base", + ], + + srcs: [ + "src/**/*.java", + ], + + static_libs: [ + "androidx.test.rules", + // mockito-target-extended-minus-junit4 used in this lib have dependency with + // jni_libs libdexmakerjvmtiagent and libstaticjvmtiagent. + "cts-net-utils", + // This is needed for androidx.test.runner.AndroidJUnitRunner. + "ctstestrunner-axt", + "junit", + "junit-params", + ], + + jni_libs: [ + // For mockito extended which is pulled in from -net-utils -> net-tests-utils + // (mockito-target-extended-minus-junit4). + "libdexmakerjvmtiagent", + "libstaticjvmtiagent", + ], + + platform_apis: true, + + // Tag this module as a mts test artifact + test_suites: [ + "general-tests", + "mts", + ], + + // Include both the 32 and 64 bit versions + compile_multilib: "both", +} diff --git a/packages/Tethering/tests/mts/AndroidManifest.xml b/packages/Tethering/tests/mts/AndroidManifest.xml new file mode 100644 index 000000000000..6d2abcad42a3 --- /dev/null +++ b/packages/Tethering/tests/mts/AndroidManifest.xml @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + --> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="android.tethering.mts"> + + <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> + <uses-permission android:name="android.permission.INTERNET"/> + + <application android:debuggable="true"> + <uses-library android:name="android.test.runner" /> + </application> + <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" + android:targetPackage="android.tethering.mts" + android:label="MTS tests of android.tethering"> + <meta-data android:name="listener" + android:value="com.android.cts.runner.CtsTestRunListener" /> + </instrumentation> + +</manifest> diff --git a/packages/Tethering/tests/mts/AndroidTest.xml b/packages/Tethering/tests/mts/AndroidTest.xml new file mode 100644 index 000000000000..80788dfa6f40 --- /dev/null +++ b/packages/Tethering/tests/mts/AndroidTest.xml @@ -0,0 +1,36 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2019 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<configuration description="Config for MTS Tethering test cases"> + <option name="test-suite-tag" value="mts" /> + <option name="config-descriptor:metadata" key="component" value="networking" /> + <!-- Instant app do not have INTERNET permission. --> + <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" /> + <!-- Feature is not backed by native code. --> + <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" /> + <!-- Allow running this against a secondary user. --> + <option name="config-descriptor:metadata" key="parameter" value="secondary_user" /> + <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller"> + <option name="cleanup-apks" value="true" /> + <option name="test-file-name" value="MtsTetheringTest.apk" /> + </target_preparer> + <test class="com.android.tradefed.testtype.AndroidJUnitTest" > + <option name="package" value="android.tethering.mts" /> + </test> + + <object type="module_controller" class="com.android.tradefed.testtype.suite.module.MainlineTestModuleController"> + <option name="mainline-module-package-name" value="com.google.android.tethering" /> + </object> +</configuration> diff --git a/packages/Tethering/tests/mts/src/android/tethering/mts/TetheringModuleTest.java b/packages/Tethering/tests/mts/src/android/tethering/mts/TetheringModuleTest.java new file mode 100644 index 000000000000..7ffe37ad648d --- /dev/null +++ b/packages/Tethering/tests/mts/src/android/tethering/mts/TetheringModuleTest.java @@ -0,0 +1,183 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.tethering.mts; + +import static android.Manifest.permission.MANAGE_TEST_NETWORKS; +import static android.Manifest.permission.NETWORK_SETTINGS; +import static android.Manifest.permission.READ_DEVICE_CONFIG; +import static android.Manifest.permission.TETHER_PRIVILEGED; +import static android.Manifest.permission.WRITE_SETTINGS; +import static android.net.cts.util.CtsTetheringUtils.isWifiTetheringSupported; +import static android.provider.DeviceConfig.NAMESPACE_CONNECTIVITY; + +import static com.android.testutils.TestNetworkTrackerKt.initTestNetwork; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.fail; +import static org.junit.Assume.assumeTrue; + +import android.app.UiAutomation; +import android.content.Context; +import android.net.IpPrefix; +import android.net.LinkAddress; +import android.net.TetheringManager; +import android.net.cts.util.CtsTetheringUtils; +import android.net.cts.util.CtsTetheringUtils.TestTetheringEventCallback; +import android.provider.DeviceConfig; + +import androidx.annotation.NonNull; +import androidx.test.InstrumentationRegistry; +import androidx.test.runner.AndroidJUnit4; + +import com.android.testutils.TestNetworkTracker; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.net.InterfaceAddress; +import java.net.NetworkInterface; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +@RunWith(AndroidJUnit4.class) +public class TetheringModuleTest { + private Context mContext; + private TetheringManager mTm; + private CtsTetheringUtils mCtsTetheringUtils; + + private UiAutomation mUiAutomation = + InstrumentationRegistry.getInstrumentation().getUiAutomation(); + + @Before + public void setUp() throws Exception { + mUiAutomation.adoptShellPermissionIdentity(MANAGE_TEST_NETWORKS, NETWORK_SETTINGS, + WRITE_SETTINGS, READ_DEVICE_CONFIG, TETHER_PRIVILEGED); + mContext = InstrumentationRegistry.getContext(); + mTm = mContext.getSystemService(TetheringManager.class); + mCtsTetheringUtils = new CtsTetheringUtils(mContext); + } + + @After + public void tearDown() throws Exception { + mUiAutomation.dropShellPermissionIdentity(); + } + + private static final String TETHER_ENABLE_SELECT_ALL_PREFIX_RANGES = + "tether_enable_select_all_prefix_ranges"; + @Test + public void testSwitchBasePrefixRangeWhenConflict() throws Exception { + assumeTrue(isFeatureEnabled(TETHER_ENABLE_SELECT_ALL_PREFIX_RANGES, true)); + + addressConflictTest(true); + } + + @Test + public void testSwitchPrefixRangeWhenConflict() throws Exception { + addressConflictTest(false); + } + + private void addressConflictTest(final boolean wholeRangeConflict) throws Exception { + final TestTetheringEventCallback tetherEventCallback = + mCtsTetheringUtils.registerTetheringEventCallback(); + + TestNetworkTracker tnt = null; + try { + tetherEventCallback.assumeTetheringSupported(); + assumeTrue(isWifiTetheringSupported(tetherEventCallback)); + + mCtsTetheringUtils.startWifiTethering(tetherEventCallback); + + final List<String> tetheredIfaces = tetherEventCallback.getTetheredInterfaces(); + assertEquals(1, tetheredIfaces.size()); + final String wifiTetheringIface = tetheredIfaces.get(0); + + NetworkInterface nif = NetworkInterface.getByName(wifiTetheringIface); + // Tethering downstream only have one ipv4 address. + final LinkAddress hotspotAddr = getFirstIpv4Address(nif); + assertNotNull(hotspotAddr); + + final IpPrefix testPrefix = getConflictingPrefix(hotspotAddr, wholeRangeConflict); + assertNotNull(testPrefix); + + tnt = setUpTestNetwork( + new LinkAddress(testPrefix.getAddress(), testPrefix.getPrefixLength())); + + tetherEventCallback.expectTetheredInterfacesChanged(null); + final List<String> wifiRegexs = + tetherEventCallback.getTetheringInterfaceRegexps().getTetherableWifiRegexs(); + + tetherEventCallback.expectTetheredInterfacesChanged(wifiRegexs); + nif = NetworkInterface.getByName(wifiTetheringIface); + final LinkAddress newHotspotAddr = getFirstIpv4Address(nif); + assertNotNull(newHotspotAddr); + + assertFalse(testPrefix.containsPrefix( + new IpPrefix(newHotspotAddr.getAddress(), newHotspotAddr.getPrefixLength()))); + + mCtsTetheringUtils.stopWifiTethering(tetherEventCallback); + } finally { + if (tnt != null) { + tnt.teardown(); + } + mTm.stopAllTethering(); + mCtsTetheringUtils.unregisterTetheringEventCallback(tetherEventCallback); + } + } + + private LinkAddress getFirstIpv4Address(final NetworkInterface nif) { + for (InterfaceAddress ia : nif.getInterfaceAddresses()) { + final LinkAddress addr = new LinkAddress(ia.getAddress(), ia.getNetworkPrefixLength()); + if (addr.isIpv4()) return addr; + } + return null; + } + + @NonNull + private IpPrefix getConflictingPrefix(final LinkAddress address, + final boolean wholeRangeConflict) { + if (!wholeRangeConflict) { + return new IpPrefix(address.getAddress(), address.getPrefixLength()); + } + + final ArrayList<IpPrefix> prefixPool = new ArrayList<>(Arrays.asList( + new IpPrefix("192.168.0.0/16"), + new IpPrefix("172.16.0.0/12"), + new IpPrefix("10.0.0.0/8"))); + + for (IpPrefix prefix : prefixPool) { + if (prefix.contains(address.getAddress())) return prefix; + } + + fail("Could not find sutiable conflict prefix"); + + // Never go here. + return null; + } + + private TestNetworkTracker setUpTestNetwork(final LinkAddress address) throws Exception { + return initTestNetwork(mContext, address, 10_000L /* test timeout ms*/); + + } + + public static boolean isFeatureEnabled(final String name, final boolean defaultValue) { + return DeviceConfig.getBoolean(NAMESPACE_CONNECTIVITY, name, defaultValue); + } +} diff --git a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/PrivateAddressCoordinatorTest.java b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/PrivateAddressCoordinatorTest.java index 86e6f11659c6..41d46e522ca4 100644 --- a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/PrivateAddressCoordinatorTest.java +++ b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/PrivateAddressCoordinatorTest.java @@ -99,29 +99,35 @@ public final class PrivateAddressCoordinatorTest { when(mContext.getSystemService(Context.CONNECTIVITY_SERVICE)).thenReturn(mConnectivityMgr); when(mConnectivityMgr.getAllNetworks()).thenReturn(mAllNetworks); when(mConfig.shouldEnableWifiP2pDedicatedIp()).thenReturn(false); + when(mConfig.isSelectAllPrefixRangeEnabled()).thenReturn(true); setUpIpServers(); - mPrivateAddressCoordinator = spy(new PrivateAddressCoordinator(mContext, mConfig, - mTetheringPrefixes)); + mPrivateAddressCoordinator = spy(new PrivateAddressCoordinator(mContext, mConfig)); + } + + private LinkAddress requestDownstreamAddress(final IpServer ipServer, boolean useLastAddress) { + final LinkAddress address = mPrivateAddressCoordinator.requestDownstreamAddress( + ipServer, useLastAddress); + when(ipServer.getAddress()).thenReturn(address); + return address; } @Test public void testRequestDownstreamAddressWithoutUsingLastAddress() throws Exception { final IpPrefix bluetoothPrefix = asIpPrefix(mBluetoothAddress); - final LinkAddress address = mPrivateAddressCoordinator.requestDownstreamAddress( - mHotspotIpServer, false /* useLastAddress */); + final LinkAddress address = requestDownstreamAddress(mHotspotIpServer, + false /* useLastAddress */); final IpPrefix hotspotPrefix = asIpPrefix(address); assertNotEquals(hotspotPrefix, bluetoothPrefix); - when(mHotspotIpServer.getAddress()).thenReturn(address); - final LinkAddress newAddress = mPrivateAddressCoordinator.requestDownstreamAddress( - mHotspotIpServer, false /* useLastAddress */); + final LinkAddress newAddress = requestDownstreamAddress(mHotspotIpServer, + false /* useLastAddress */); final IpPrefix testDupRequest = asIpPrefix(newAddress); assertNotEquals(hotspotPrefix, testDupRequest); assertNotEquals(bluetoothPrefix, testDupRequest); mPrivateAddressCoordinator.releaseDownstream(mHotspotIpServer); - final LinkAddress usbAddress = mPrivateAddressCoordinator.requestDownstreamAddress( - mUsbIpServer, false /* useLastAddress */); + final LinkAddress usbAddress = requestDownstreamAddress(mUsbIpServer, + false /* useLastAddress */); final IpPrefix usbPrefix = asIpPrefix(usbAddress); assertNotEquals(usbPrefix, bluetoothPrefix); assertNotEquals(usbPrefix, hotspotPrefix); @@ -132,29 +138,26 @@ public final class PrivateAddressCoordinatorTest { public void testSanitizedAddress() throws Exception { int fakeSubAddr = 0x2b00; // 43.0. when(mPrivateAddressCoordinator.getRandomInt()).thenReturn(fakeSubAddr); - LinkAddress actualAddress = mPrivateAddressCoordinator.requestDownstreamAddress( - mHotspotIpServer, false /* useLastAddress */); + LinkAddress actualAddress = requestDownstreamAddress(mHotspotIpServer, + false /* useLastAddress */); assertEquals(new LinkAddress("192.168.43.2/24"), actualAddress); mPrivateAddressCoordinator.releaseDownstream(mHotspotIpServer); fakeSubAddr = 0x2d01; // 45.1. when(mPrivateAddressCoordinator.getRandomInt()).thenReturn(fakeSubAddr); - actualAddress = mPrivateAddressCoordinator.requestDownstreamAddress( - mHotspotIpServer, false /* useLastAddress */); + actualAddress = requestDownstreamAddress(mHotspotIpServer, false /* useLastAddress */); assertEquals(new LinkAddress("192.168.45.2/24"), actualAddress); mPrivateAddressCoordinator.releaseDownstream(mHotspotIpServer); fakeSubAddr = 0x2eff; // 46.255. when(mPrivateAddressCoordinator.getRandomInt()).thenReturn(fakeSubAddr); - actualAddress = mPrivateAddressCoordinator.requestDownstreamAddress( - mHotspotIpServer, false /* useLastAddress */); + actualAddress = requestDownstreamAddress(mHotspotIpServer, false /* useLastAddress */); assertEquals(new LinkAddress("192.168.46.254/24"), actualAddress); mPrivateAddressCoordinator.releaseDownstream(mHotspotIpServer); fakeSubAddr = 0x2f05; // 47.5. when(mPrivateAddressCoordinator.getRandomInt()).thenReturn(fakeSubAddr); - actualAddress = mPrivateAddressCoordinator.requestDownstreamAddress( - mHotspotIpServer, false /* useLastAddress */); + actualAddress = requestDownstreamAddress(mHotspotIpServer, false /* useLastAddress */); assertEquals(new LinkAddress("192.168.47.5/24"), actualAddress); mPrivateAddressCoordinator.releaseDownstream(mHotspotIpServer); } @@ -164,8 +167,8 @@ public final class PrivateAddressCoordinatorTest { // - Test bluetooth prefix is reserved. when(mPrivateAddressCoordinator.getRandomInt()).thenReturn( getSubAddress(mBluetoothAddress.getAddress().getAddress())); - final LinkAddress hotspotAddress = mPrivateAddressCoordinator.requestDownstreamAddress( - mHotspotIpServer, false /* useLastAddress */); + final LinkAddress hotspotAddress = requestDownstreamAddress(mHotspotIpServer, + false /* useLastAddress */); final IpPrefix hotspotPrefix = asIpPrefix(hotspotAddress); assertNotEquals(asIpPrefix(mBluetoothAddress), hotspotPrefix); mPrivateAddressCoordinator.releaseDownstream(mHotspotIpServer); @@ -173,8 +176,8 @@ public final class PrivateAddressCoordinatorTest { // - Test previous enabled hotspot prefix(cached prefix) is reserved. when(mPrivateAddressCoordinator.getRandomInt()).thenReturn( getSubAddress(hotspotAddress.getAddress().getAddress())); - final LinkAddress usbAddress = mPrivateAddressCoordinator.requestDownstreamAddress( - mUsbIpServer, false /* useLastAddress */); + final LinkAddress usbAddress = requestDownstreamAddress(mUsbIpServer, + false /* useLastAddress */); final IpPrefix usbPrefix = asIpPrefix(usbAddress); assertNotEquals(asIpPrefix(mBluetoothAddress), usbPrefix); assertNotEquals(hotspotPrefix, usbPrefix); @@ -183,8 +186,8 @@ public final class PrivateAddressCoordinatorTest { // - Test wifi p2p prefix is reserved. when(mPrivateAddressCoordinator.getRandomInt()).thenReturn( getSubAddress(mLegacyWifiP2pAddress.getAddress().getAddress())); - final LinkAddress etherAddress = mPrivateAddressCoordinator.requestDownstreamAddress( - mEthernetIpServer, false /* useLastAddress */); + final LinkAddress etherAddress = requestDownstreamAddress(mEthernetIpServer, + false /* useLastAddress */); final IpPrefix etherPrefix = asIpPrefix(etherAddress); assertNotEquals(asIpPrefix(mLegacyWifiP2pAddress), etherPrefix); assertNotEquals(asIpPrefix(mBluetoothAddress), etherPrefix); @@ -194,17 +197,15 @@ public final class PrivateAddressCoordinatorTest { @Test public void testRequestLastDownstreamAddress() throws Exception { - final int fakeHotspotSubAddr = 0x2b05; - final IpPrefix predefinedPrefix = new IpPrefix("192.168.43.0/24"); + final int fakeHotspotSubAddr = 0x2b05; // 43.5 when(mPrivateAddressCoordinator.getRandomInt()).thenReturn(fakeHotspotSubAddr); - final LinkAddress hotspotAddress = mPrivateAddressCoordinator.requestDownstreamAddress( - mHotspotIpServer, true /* useLastAddress */); - assertEquals("Wrong wifi prefix: ", predefinedPrefix, asIpPrefix(hotspotAddress)); - when(mHotspotIpServer.getAddress()).thenReturn(hotspotAddress); + final LinkAddress hotspotAddress = requestDownstreamAddress(mHotspotIpServer, + true /* useLastAddress */); + assertEquals("Wrong wifi prefix: ", new LinkAddress("192.168.43.5/24"), hotspotAddress); - final LinkAddress usbAddress = mPrivateAddressCoordinator.requestDownstreamAddress( - mUsbIpServer, true /* useLastAddress */); - assertNotEquals(predefinedPrefix, asIpPrefix(usbAddress)); + final LinkAddress usbAddress = requestDownstreamAddress(mUsbIpServer, + true /* useLastAddress */); + assertEquals("Wrong wifi prefix: ", new LinkAddress("192.168.45.5/24"), usbAddress); mPrivateAddressCoordinator.releaseDownstream(mHotspotIpServer); mPrivateAddressCoordinator.releaseDownstream(mUsbIpServer); @@ -212,12 +213,19 @@ public final class PrivateAddressCoordinatorTest { final int newFakeSubAddr = 0x3c05; when(mPrivateAddressCoordinator.getRandomInt()).thenReturn(fakeHotspotSubAddr); - final LinkAddress newHotspotAddress = mPrivateAddressCoordinator.requestDownstreamAddress( - mHotspotIpServer, true /* useLastAddress */); + final LinkAddress newHotspotAddress = requestDownstreamAddress(mHotspotIpServer, + true /* useLastAddress */); assertEquals(hotspotAddress, newHotspotAddress); - final LinkAddress newUsbAddress = mPrivateAddressCoordinator.requestDownstreamAddress( - mUsbIpServer, true /* useLastAddress */); + final LinkAddress newUsbAddress = requestDownstreamAddress(mUsbIpServer, + true /* useLastAddress */); assertEquals(usbAddress, newUsbAddress); + + final UpstreamNetworkState wifiUpstream = buildUpstreamNetworkState(mWifiNetwork, + new LinkAddress("192.168.88.23/16"), null, + makeNetworkCapabilities(TRANSPORT_WIFI)); + mPrivateAddressCoordinator.updateUpstreamPrefix(wifiUpstream); + verify(mHotspotIpServer).sendMessage(IpServer.CMD_NOTIFY_PREFIX_CONFLICT); + verify(mUsbIpServer).sendMessage(IpServer.CMD_NOTIFY_PREFIX_CONFLICT); } private UpstreamNetworkState buildUpstreamNetworkState(final Network network, @@ -248,11 +256,10 @@ public final class PrivateAddressCoordinatorTest { // Force always get subAddress "43.5" for conflict testing. when(mPrivateAddressCoordinator.getRandomInt()).thenReturn(fakeHotspotSubAddr); // - Enable hotspot with prefix 192.168.43.0/24 - final LinkAddress hotspotAddr = mPrivateAddressCoordinator.requestDownstreamAddress( - mHotspotIpServer, true /* useLastAddress */); + final LinkAddress hotspotAddr = requestDownstreamAddress(mHotspotIpServer, + true /* useLastAddress */); final IpPrefix hotspotPrefix = asIpPrefix(hotspotAddr); assertEquals("Wrong wifi prefix: ", predefinedPrefix, hotspotPrefix); - when(mHotspotIpServer.getAddress()).thenReturn(hotspotAddr); // - test mobile network with null NetworkCapabilities. Ideally this should not happen // because NetworkCapabilities update should always happen before LinkProperties update // and the UpstreamNetworkState update, just make sure no crash in this case. @@ -303,24 +310,22 @@ public final class PrivateAddressCoordinatorTest { reset(mHotspotIpServer); // - Restart hotspot again and its prefix is different previous. mPrivateAddressCoordinator.releaseDownstream(mHotspotIpServer); - final LinkAddress hotspotAddr2 = mPrivateAddressCoordinator.requestDownstreamAddress( - mHotspotIpServer, true /* useLastAddress */); + final LinkAddress hotspotAddr2 = requestDownstreamAddress(mHotspotIpServer, + true /* useLastAddress */); final IpPrefix hotspotPrefix2 = asIpPrefix(hotspotAddr2); assertNotEquals(hotspotPrefix, hotspotPrefix2); - when(mHotspotIpServer.getAddress()).thenReturn(hotspotAddr2); mPrivateAddressCoordinator.updateUpstreamPrefix(v4OnlyWifi); verify(mHotspotIpServer, never()).sendMessage(IpServer.CMD_NOTIFY_PREFIX_CONFLICT); // - Usb tethering can be enabled and its prefix is different with conflict one. - final LinkAddress usbAddr = mPrivateAddressCoordinator.requestDownstreamAddress( - mUsbIpServer, true /* useLastAddress */); + final LinkAddress usbAddr = requestDownstreamAddress(mUsbIpServer, + true /* useLastAddress */); final IpPrefix usbPrefix = asIpPrefix(usbAddr); assertNotEquals(predefinedPrefix, usbPrefix); assertNotEquals(hotspotPrefix2, usbPrefix); - when(mUsbIpServer.getAddress()).thenReturn(usbAddr); // - Disable wifi upstream, then wifi's prefix can be selected again. mPrivateAddressCoordinator.removeUpstreamPrefix(mWifiNetwork); - final LinkAddress ethAddr = mPrivateAddressCoordinator.requestDownstreamAddress( - mEthernetIpServer, true /* useLastAddress */); + final LinkAddress ethAddr = requestDownstreamAddress(mEthernetIpServer, + true /* useLastAddress */); final IpPrefix ethPrefix = asIpPrefix(ethAddr); assertEquals(predefinedPrefix, ethPrefix); } @@ -329,21 +334,19 @@ public final class PrivateAddressCoordinatorTest { public void testChooseAvailablePrefix() throws Exception { final int randomAddress = 0x8605; // 134.5 when(mPrivateAddressCoordinator.getRandomInt()).thenReturn(randomAddress); - final LinkAddress addr0 = mPrivateAddressCoordinator.requestDownstreamAddress( - mHotspotIpServer, true/* useLastAddress */); + final LinkAddress addr0 = requestDownstreamAddress(mHotspotIpServer, + true /* useLastAddress */); // Check whether return address is prefix 192.168.0.0/16 + subAddress 0.0.134.5. assertEquals("Wrong prefix: ", new LinkAddress("192.168.134.5/24"), addr0); - when(mHotspotIpServer.getAddress()).thenReturn(addr0); final UpstreamNetworkState wifiUpstream = buildUpstreamNetworkState(mWifiNetwork, new LinkAddress("192.168.134.13/26"), null, makeNetworkCapabilities(TRANSPORT_WIFI)); mPrivateAddressCoordinator.updateUpstreamPrefix(wifiUpstream); // Check whether return address is next prefix of 192.168.134.0/24. - final LinkAddress addr1 = mPrivateAddressCoordinator.requestDownstreamAddress( - mHotspotIpServer, true/* useLastAddress */); + final LinkAddress addr1 = requestDownstreamAddress(mHotspotIpServer, + true /* useLastAddress */); assertEquals("Wrong prefix: ", new LinkAddress("192.168.135.5/24"), addr1); - when(mHotspotIpServer.getAddress()).thenReturn(addr1); final UpstreamNetworkState wifiUpstream2 = buildUpstreamNetworkState(mWifiNetwork, new LinkAddress("192.168.149.16/19"), null, makeNetworkCapabilities(TRANSPORT_WIFI)); @@ -351,10 +354,9 @@ public final class PrivateAddressCoordinatorTest { // The conflict range is 128 ~ 159, so the address is 192.168.160.5/24. - final LinkAddress addr2 = mPrivateAddressCoordinator.requestDownstreamAddress( - mHotspotIpServer, true/* useLastAddress */); + final LinkAddress addr2 = requestDownstreamAddress(mHotspotIpServer, + true /* useLastAddress */); assertEquals("Wrong prefix: ", new LinkAddress("192.168.160.5/24"), addr2); - when(mHotspotIpServer.getAddress()).thenReturn(addr2); final UpstreamNetworkState mobileUpstream = buildUpstreamNetworkState(mMobileNetwork, new LinkAddress("192.168.129.53/18"), null, makeNetworkCapabilities(TRANSPORT_CELLULAR)); @@ -367,10 +369,9 @@ public final class PrivateAddressCoordinatorTest { mPrivateAddressCoordinator.updateUpstreamPrefix(mobileUpstream2); // The conflict range are 128 ~ 159 and 159 ~ 191, so the address is 192.168.192.5/24. - final LinkAddress addr3 = mPrivateAddressCoordinator.requestDownstreamAddress( - mHotspotIpServer, true/* useLastAddress */); + final LinkAddress addr3 = requestDownstreamAddress(mHotspotIpServer, + true /* useLastAddress */); assertEquals("Wrong prefix: ", new LinkAddress("192.168.192.5/24"), addr3); - when(mHotspotIpServer.getAddress()).thenReturn(addr3); final UpstreamNetworkState mobileUpstream3 = buildUpstreamNetworkState(mMobileNetwork3, new LinkAddress("192.168.188.133/17"), null, makeNetworkCapabilities(TRANSPORT_CELLULAR)); @@ -378,20 +379,18 @@ public final class PrivateAddressCoordinatorTest { // Conflict range: 128 ~ 255. The next available address is 192.168.0.5 because // 192.168.134/24 ~ 192.168.255.255/24 is not available. - final LinkAddress addr4 = mPrivateAddressCoordinator.requestDownstreamAddress( - mHotspotIpServer, true/* useLastAddress */); + final LinkAddress addr4 = requestDownstreamAddress(mHotspotIpServer, + true /* useLastAddress */); assertEquals("Wrong prefix: ", new LinkAddress("192.168.0.5/24"), addr4); - when(mHotspotIpServer.getAddress()).thenReturn(addr4); final UpstreamNetworkState mobileUpstream4 = buildUpstreamNetworkState(mMobileNetwork4, new LinkAddress("192.168.3.59/21"), null, makeNetworkCapabilities(TRANSPORT_CELLULAR)); mPrivateAddressCoordinator.updateUpstreamPrefix(mobileUpstream4); // Conflict ranges: 128 ~ 255 and 0 ~ 7, so the address is 192.168.8.5/24. - final LinkAddress addr5 = mPrivateAddressCoordinator.requestDownstreamAddress( - mHotspotIpServer, true/* useLastAddress */); + final LinkAddress addr5 = requestDownstreamAddress(mHotspotIpServer, + true /* useLastAddress */); assertEquals("Wrong prefix: ", new LinkAddress("192.168.8.5/24"), addr5); - when(mHotspotIpServer.getAddress()).thenReturn(addr5); final UpstreamNetworkState mobileUpstream5 = buildUpstreamNetworkState(mMobileNetwork5, new LinkAddress("192.168.68.43/21"), null, makeNetworkCapabilities(TRANSPORT_CELLULAR)); @@ -399,41 +398,37 @@ public final class PrivateAddressCoordinatorTest { // Update an upstream that does *not* conflict, check whether return the same address // 192.168.5/24. - final LinkAddress addr6 = mPrivateAddressCoordinator.requestDownstreamAddress( - mHotspotIpServer, true/* useLastAddress */); + final LinkAddress addr6 = requestDownstreamAddress(mHotspotIpServer, + true /* useLastAddress */); assertEquals("Wrong prefix: ", new LinkAddress("192.168.8.5/24"), addr6); - when(mHotspotIpServer.getAddress()).thenReturn(addr6); final UpstreamNetworkState mobileUpstream6 = buildUpstreamNetworkState(mMobileNetwork6, new LinkAddress("192.168.10.97/21"), null, makeNetworkCapabilities(TRANSPORT_CELLULAR)); mPrivateAddressCoordinator.updateUpstreamPrefix(mobileUpstream6); // Conflict ranges: 0 ~ 15 and 128 ~ 255, so the address is 192.168.16.5/24. - final LinkAddress addr7 = mPrivateAddressCoordinator.requestDownstreamAddress( - mHotspotIpServer, true/* useLastAddress */); + final LinkAddress addr7 = requestDownstreamAddress(mHotspotIpServer, + true /* useLastAddress */); assertEquals("Wrong prefix: ", new LinkAddress("192.168.16.5/24"), addr7); - when(mHotspotIpServer.getAddress()).thenReturn(addr7); final UpstreamNetworkState mobileUpstream7 = buildUpstreamNetworkState(mMobileNetwork6, new LinkAddress("192.168.0.0/17"), null, makeNetworkCapabilities(TRANSPORT_CELLULAR)); mPrivateAddressCoordinator.updateUpstreamPrefix(mobileUpstream7); // Choose prefix from next range(172.16.0.0/12) when no available prefix in 192.168.0.0/16. - final LinkAddress addr8 = mPrivateAddressCoordinator.requestDownstreamAddress( - mHotspotIpServer, true/* useLastAddress */); + final LinkAddress addr8 = requestDownstreamAddress(mHotspotIpServer, + true /* useLastAddress */); assertEquals("Wrong prefix: ", new LinkAddress("172.16.134.5/24"), addr8); - when(mHotspotIpServer.getAddress()).thenReturn(addr6); } @Test public void testChoosePrefixFromDifferentRanges() throws Exception { final int randomAddress = 0x1f2b2a; // 31.43.42 when(mPrivateAddressCoordinator.getRandomInt()).thenReturn(randomAddress); - final LinkAddress classC1 = mPrivateAddressCoordinator.requestDownstreamAddress( - mHotspotIpServer, true/* useLastAddress */); + final LinkAddress classC1 = requestDownstreamAddress(mHotspotIpServer, + true /* useLastAddress */); // Check whether return address is prefix 192.168.0.0/16 + subAddress 0.0.43.42. assertEquals("Wrong prefix: ", new LinkAddress("192.168.43.42/24"), classC1); - when(mHotspotIpServer.getAddress()).thenReturn(classC1); final UpstreamNetworkState wifiUpstream = buildUpstreamNetworkState(mWifiNetwork, new LinkAddress("192.168.88.23/17"), null, makeNetworkCapabilities(TRANSPORT_WIFI)); @@ -441,10 +436,9 @@ public final class PrivateAddressCoordinatorTest { verifyNotifyConflictAndRelease(mHotspotIpServer); // Check whether return address is next address of prefix 192.168.128.0/17. - final LinkAddress classC2 = mPrivateAddressCoordinator.requestDownstreamAddress( - mHotspotIpServer, true/* useLastAddress */); + final LinkAddress classC2 = requestDownstreamAddress(mHotspotIpServer, + true /* useLastAddress */); assertEquals("Wrong prefix: ", new LinkAddress("192.168.128.42/24"), classC2); - when(mHotspotIpServer.getAddress()).thenReturn(classC2); final UpstreamNetworkState mobileUpstream = buildUpstreamNetworkState(mMobileNetwork, new LinkAddress("192.1.2.3/8"), null, makeNetworkCapabilities(TRANSPORT_CELLULAR)); @@ -452,10 +446,9 @@ public final class PrivateAddressCoordinatorTest { verifyNotifyConflictAndRelease(mHotspotIpServer); // Check whether return address is under prefix 172.16.0.0/12. - final LinkAddress classB1 = mPrivateAddressCoordinator.requestDownstreamAddress( - mHotspotIpServer, true/* useLastAddress */); + final LinkAddress classB1 = requestDownstreamAddress(mHotspotIpServer, + true /* useLastAddress */); assertEquals("Wrong prefix: ", new LinkAddress("172.31.43.42/24"), classB1); - when(mHotspotIpServer.getAddress()).thenReturn(classB1); final UpstreamNetworkState mobileUpstream2 = buildUpstreamNetworkState(mMobileNetwork2, new LinkAddress("172.28.123.100/14"), null, makeNetworkCapabilities(TRANSPORT_CELLULAR)); @@ -464,16 +457,14 @@ public final class PrivateAddressCoordinatorTest { // 172.28.0.0 ~ 172.31.255.255 is not available. // Check whether return address is next address of prefix 172.16.0.0/14. - final LinkAddress classB2 = mPrivateAddressCoordinator.requestDownstreamAddress( - mHotspotIpServer, true/* useLastAddress */); + final LinkAddress classB2 = requestDownstreamAddress(mHotspotIpServer, + true /* useLastAddress */); assertEquals("Wrong prefix: ", new LinkAddress("172.16.0.42/24"), classB2); - when(mHotspotIpServer.getAddress()).thenReturn(classB2); // Check whether new downstream is next address of address 172.16.0.42/24. - final LinkAddress classB3 = mPrivateAddressCoordinator.requestDownstreamAddress( - mUsbIpServer, true/* useLastAddress */); + final LinkAddress classB3 = requestDownstreamAddress(mUsbIpServer, + true /* useLastAddress */); assertEquals("Wrong prefix: ", new LinkAddress("172.16.1.42/24"), classB3); - when(mUsbIpServer.getAddress()).thenReturn(classB3); final UpstreamNetworkState mobileUpstream3 = buildUpstreamNetworkState(mMobileNetwork3, new LinkAddress("172.16.0.1/24"), null, makeNetworkCapabilities(TRANSPORT_CELLULAR)); @@ -482,10 +473,9 @@ public final class PrivateAddressCoordinatorTest { verify(mUsbIpServer, never()).sendMessage(IpServer.CMD_NOTIFY_PREFIX_CONFLICT); // Check whether return address is next address of prefix 172.16.1.42/24. - final LinkAddress classB4 = mPrivateAddressCoordinator.requestDownstreamAddress( - mHotspotIpServer, true/* useLastAddress */); + final LinkAddress classB4 = requestDownstreamAddress(mHotspotIpServer, + true /* useLastAddress */); assertEquals("Wrong prefix: ", new LinkAddress("172.16.2.42/24"), classB4); - when(mHotspotIpServer.getAddress()).thenReturn(classB4); final UpstreamNetworkState mobileUpstream4 = buildUpstreamNetworkState(mMobileNetwork4, new LinkAddress("172.16.0.1/13"), null, makeNetworkCapabilities(TRANSPORT_CELLULAR)); @@ -494,15 +484,13 @@ public final class PrivateAddressCoordinatorTest { verifyNotifyConflictAndRelease(mUsbIpServer); // Check whether return address is next address of prefix 172.16.0.1/13. - final LinkAddress classB5 = mPrivateAddressCoordinator.requestDownstreamAddress( - mHotspotIpServer, true/* useLastAddress */); + final LinkAddress classB5 = requestDownstreamAddress(mHotspotIpServer, + true /* useLastAddress */); assertEquals("Wrong prefix: ", new LinkAddress("172.24.0.42/24"), classB5); - when(mHotspotIpServer.getAddress()).thenReturn(classB5); // Check whether return address is next address of prefix 172.24.0.42/24. - final LinkAddress classB6 = mPrivateAddressCoordinator.requestDownstreamAddress( - mUsbIpServer, true/* useLastAddress */); + final LinkAddress classB6 = requestDownstreamAddress(mUsbIpServer, + true /* useLastAddress */); assertEquals("Wrong prefix: ", new LinkAddress("172.24.1.42/24"), classB6); - when(mUsbIpServer.getAddress()).thenReturn(classB6); final UpstreamNetworkState mobileUpstream5 = buildUpstreamNetworkState(mMobileNetwork5, new LinkAddress("172.24.0.1/12"), null, makeNetworkCapabilities(TRANSPORT_CELLULAR)); @@ -511,13 +499,12 @@ public final class PrivateAddressCoordinatorTest { verifyNotifyConflictAndRelease(mUsbIpServer); // Check whether return address is prefix 10.0.0.0/8 + subAddress 0.31.43.42. - final LinkAddress classA1 = mPrivateAddressCoordinator.requestDownstreamAddress( - mHotspotIpServer, true/* useLastAddress */); + final LinkAddress classA1 = requestDownstreamAddress(mHotspotIpServer, + true /* useLastAddress */); assertEquals("Wrong prefix: ", new LinkAddress("10.31.43.42/24"), classA1); - when(mHotspotIpServer.getAddress()).thenReturn(classA1); // Check whether new downstream is next address of address 10.31.43.42/24. - final LinkAddress classA2 = mPrivateAddressCoordinator.requestDownstreamAddress( - mUsbIpServer, true/* useLastAddress */); + final LinkAddress classA2 = requestDownstreamAddress(mUsbIpServer, + true /* useLastAddress */); assertEquals("Wrong prefix: ", new LinkAddress("10.31.44.42/24"), classA2); } @@ -536,8 +523,8 @@ public final class PrivateAddressCoordinatorTest { } private void assertReseveredWifiP2pPrefix() throws Exception { - LinkAddress address = mPrivateAddressCoordinator.requestDownstreamAddress( - mHotspotIpServer, true /* useLastAddress */); + LinkAddress address = requestDownstreamAddress(mHotspotIpServer, + true /* useLastAddress */); final IpPrefix hotspotPrefix = asIpPrefix(address); final IpPrefix legacyWifiP2pPrefix = asIpPrefix(mLegacyWifiP2pAddress); assertNotEquals(legacyWifiP2pPrefix, hotspotPrefix); @@ -556,8 +543,8 @@ public final class PrivateAddressCoordinatorTest { assertReseveredWifiP2pPrefix(); // If #shouldEnableWifiP2pDedicatedIp() is enabled, wifi P2P gets the configured address. - LinkAddress address = mPrivateAddressCoordinator.requestDownstreamAddress( - mWifiP2pIpServer, true /* useLastAddress */); + LinkAddress address = requestDownstreamAddress(mWifiP2pIpServer, + true /* useLastAddress */); assertEquals(mLegacyWifiP2pAddress, address); mPrivateAddressCoordinator.releaseDownstream(mWifiP2pIpServer); } diff --git a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java index dc0940cc0222..237e2c27bfa1 100644 --- a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java +++ b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringConfigurationTest.java @@ -131,6 +131,7 @@ public class TetheringConfigurationTest { when(mResources.getBoolean(R.bool.config_tether_enable_legacy_wifi_p2p_dedicated_ip)) .thenReturn(false); initializeBpfOffloadConfiguration(true, null /* unset */); + initEnableSelectAllPrefixRangeFlag(null /* unset */); mHasTelephonyManager = true; mMockContext = new MockContext(mContext); @@ -428,4 +429,30 @@ public class TetheringConfigurationTest { mMockContext, mLog, INVALID_SUBSCRIPTION_ID); assertTrue(testCfg.shouldEnableWifiP2pDedicatedIp()); } + + private void initEnableSelectAllPrefixRangeFlag(final String value) { + doReturn(value).when( + () -> DeviceConfig.getProperty(eq(NAMESPACE_CONNECTIVITY), + eq(TetheringConfiguration.TETHER_ENABLE_SELECT_ALL_PREFIX_RANGES))); + } + + @Test + public void testSelectAllPrefixRangeFlag() throws Exception { + // Test default value. + final TetheringConfiguration defaultCfg = new TetheringConfiguration( + mMockContext, mLog, INVALID_SUBSCRIPTION_ID); + assertTrue(defaultCfg.isSelectAllPrefixRangeEnabled()); + + // Test disable flag. + initEnableSelectAllPrefixRangeFlag("false"); + final TetheringConfiguration testDisable = new TetheringConfiguration( + mMockContext, mLog, INVALID_SUBSCRIPTION_ID); + assertFalse(testDisable.isSelectAllPrefixRangeEnabled()); + + // Test enable flag. + initEnableSelectAllPrefixRangeFlag("true"); + final TetheringConfiguration testEnable = new TetheringConfiguration( + mMockContext, mLog, INVALID_SUBSCRIPTION_ID); + assertTrue(testEnable.isSelectAllPrefixRangeEnabled()); + } } diff --git a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java index 20e94b256ad1..114cb7ca6ec7 100644 --- a/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java +++ b/packages/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java @@ -456,11 +456,7 @@ public class TetheringTest { @Override public PrivateAddressCoordinator getPrivateAddressCoordinator(Context ctx, TetheringConfiguration cfg) { - final ArrayList<IpPrefix> prefixPool = new ArrayList<>(Arrays.asList( - new IpPrefix("192.168.0.0/16"), - new IpPrefix("172.16.0.0/12"), - new IpPrefix("10.0.0.0/8"))); - mPrivateAddressCoordinator = spy(new PrivateAddressCoordinator(ctx, cfg, prefixPool)); + mPrivateAddressCoordinator = super.getPrivateAddressCoordinator(ctx, cfg); return mPrivateAddressCoordinator; } } diff --git a/services/Android.bp b/services/Android.bp index 33205c0d20fe..8c9c4875b835 100644 --- a/services/Android.bp +++ b/services/Android.bp @@ -92,6 +92,7 @@ java_library { "services.voiceinteraction", "services.wifi", "service-blobstore", + "service-connectivity", "service-jobscheduler", "android.hidl.base-V1.0-java", ], diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java index a167ab16f944..d6d4e4f6c746 100644 --- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java +++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java @@ -150,6 +150,8 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ private boolean mRequestTwoFingerPassthrough; + private boolean mSendMotionEvents; + boolean mRequestFilterKeyEvents; boolean mRetrieveInteractiveWindows; @@ -329,6 +331,8 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ & AccessibilityServiceInfo.FLAG_REQUEST_MULTI_FINGER_GESTURES) != 0; mRequestTwoFingerPassthrough = (info.flags & AccessibilityServiceInfo.FLAG_REQUEST_2_FINGER_PASSTHROUGH) != 0; + mSendMotionEvents = + (info.flags & AccessibilityServiceInfo.FLAG_SEND_MOTION_EVENTS) != 0; mRequestFilterKeyEvents = (info.flags & AccessibilityServiceInfo.FLAG_REQUEST_FILTER_KEY_EVENTS) != 0; mRetrieveInteractiveWindows = (info.flags @@ -1780,6 +1784,10 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ return mRequestTwoFingerPassthrough; } + public boolean isSendMotionEventsEnabled() { + return mSendMotionEvents; + } + @Override public void setGestureDetectionPassthroughRegion(int displayId, Region region) { mSystemSupport.setGestureDetectionPassthroughRegion(displayId, region); diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java index cd9ab8db0854..857ac6ae62a9 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java @@ -119,12 +119,19 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo static final int FLAG_REQUEST_MULTI_FINGER_GESTURES = 0x00000100; /** - * Flag for enabling multi-finger gestures. + * Flag for enabling two-finger passthrough when multi-finger gestures are enabled. * * @see #setUserAndEnabledFeatures(int, int) */ static final int FLAG_REQUEST_2_FINGER_PASSTHROUGH = 0x00000200; + /** + * Flag for including motion events when dispatching a gesture. + * + * @see #setUserAndEnabledFeatures(int, int) + */ + static final int FLAG_SEND_MOTION_EVENTS = 0x00000400; + static final int FEATURES_AFFECTING_MOTION_EVENTS = FLAG_FEATURE_INJECT_MOTION_EVENTS | FLAG_FEATURE_AUTOCLICK @@ -432,6 +439,9 @@ class AccessibilityInputFilter extends InputFilter implements EventStreamTransfo if ((mEnabledFeatures & FLAG_REQUEST_2_FINGER_PASSTHROUGH) != 0) { explorer.setTwoFingerPassthroughEnabled(true); } + if ((mEnabledFeatures & FLAG_SEND_MOTION_EVENTS) != 0) { + explorer.setSendMotionEventsEnabled(true); + } addFirstEventHandler(displayId, explorer); mTouchExplorer.put(displayId, explorer); } diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index 9ddf7a4fb79b..35481a282701 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -941,10 +941,19 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub if (resolvedUserId != mCurrentUserId) { return null; } - if (mA11yWindowManager.findA11yWindowInfoByIdLocked(windowId) == null) { + final AccessibilityWindowInfo accessibilityWindowInfo = mA11yWindowManager + .findA11yWindowInfoByIdLocked(windowId); + if (accessibilityWindowInfo == null) { return null; } - return mA11yWindowManager.getWindowTokenForUserAndWindowIdLocked(userId, windowId); + // We use AccessibilityWindowInfo#getId instead of windowId. When the windowId comes + // from an embedded hierarchy, the system can't find correct window token because + // embedded hierarchy doesn't have windowInfo. Calling + // AccessibilityWindowManager#findA11yWindowInfoByIdLocked can look for its parent's + // windowInfo, so it is safer to use AccessibilityWindowInfo#getId + // to get window token to find real window. + return mA11yWindowManager.getWindowTokenForUserAndWindowIdLocked(userId, + accessibilityWindowInfo.getId()); } } @@ -1858,6 +1867,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub if (userState.isFilterKeyEventsEnabledLocked()) { flags |= AccessibilityInputFilter.FLAG_FEATURE_FILTER_KEY_EVENTS; } + if (userState.isSendMotionEventsEnabled()) { + flags |= AccessibilityInputFilter.FLAG_SEND_MOTION_EVENTS; + } + if (userState.isAutoclickEnabledLocked()) { flags |= AccessibilityInputFilter.FLAG_FEATURE_AUTOCLICK; } @@ -2138,6 +2151,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub boolean serviceHandlesDoubleTapEnabled = false; boolean requestMultiFingerGestures = false; boolean requestTwoFingerPassthrough = false; + boolean sendMotionEvents = false; final int serviceCount = userState.mBoundServices.size(); for (int i = 0; i < serviceCount; i++) { AccessibilityServiceConnection service = userState.mBoundServices.get(i); @@ -2146,6 +2160,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub serviceHandlesDoubleTapEnabled = service.isServiceHandlesDoubleTapEnabled(); requestMultiFingerGestures = service.isMultiFingerGesturesEnabled(); requestTwoFingerPassthrough = service.isTwoFingerPassthroughEnabled(); + sendMotionEvents = service.isSendMotionEventsEnabled(); break; } } @@ -2163,6 +2178,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub userState.setServiceHandlesDoubleTapLocked(serviceHandlesDoubleTapEnabled); userState.setMultiFingerGesturesLocked(requestMultiFingerGestures); userState.setTwoFingerPassthroughLocked(requestTwoFingerPassthrough); + userState.setSendMotionEventsEnabled(sendMotionEvents); } private boolean readAccessibilityShortcutKeySettingLocked(AccessibilityUserState userState) { @@ -2624,6 +2640,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } @Override + @SuppressWarnings("AndroidFrameworkPendingIntentMutability") public PendingIntent getPendingIntentActivity(Context context, int requestCode, Intent intent, int flags) { return PendingIntent.getActivity(context, requestCode, intent, flags); diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java index 4c9e44403026..240c7ff061c8 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java @@ -111,6 +111,7 @@ class AccessibilityUserState { private boolean mServiceHandlesDoubleTap; private boolean mRequestMultiFingerGestures; private boolean mRequestTwoFingerPassthrough; + private boolean mSendMotionEventsEnabled; private int mUserInteractiveUiTimeout; private int mUserNonInteractiveUiTimeout; private int mNonInteractiveUiTimeout = 0; @@ -171,6 +172,7 @@ class AccessibilityUserState { mServiceHandlesDoubleTap = false; mRequestMultiFingerGestures = false; mRequestTwoFingerPassthrough = false; + mSendMotionEventsEnabled = false; mIsDisplayMagnificationEnabled = false; mIsAutoclickEnabled = false; mUserNonInteractiveUiTimeout = 0; @@ -460,6 +462,7 @@ class AccessibilityUserState { .append(String.valueOf(mRequestMultiFingerGestures)); pw.append(", requestTwoFingerPassthrough=") .append(String.valueOf(mRequestTwoFingerPassthrough)); + pw.append(", sendMotionEventsEnabled").append(String.valueOf(mSendMotionEventsEnabled)); pw.append(", displayMagnificationEnabled=").append(String.valueOf( mIsDisplayMagnificationEnabled)); pw.append(", autoclickEnabled=").append(String.valueOf(mIsAutoclickEnabled)); @@ -802,6 +805,13 @@ class AccessibilityUserState { mRequestTwoFingerPassthrough = enabled; } + public boolean isSendMotionEventsEnabled() { + return mSendMotionEventsEnabled; + } + + public void setSendMotionEventsEnabled(boolean mode) { + mSendMotionEventsEnabled = mode; + } public int getUserInteractiveUiTimeoutLocked() { return mUserInteractiveUiTimeout; diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/GestureManifold.java b/services/accessibility/java/com/android/server/accessibility/gestures/GestureManifold.java index a860db389d1e..2c38dc330a62 100644 --- a/services/accessibility/java/com/android/server/accessibility/gestures/GestureManifold.java +++ b/services/accessibility/java/com/android/server/accessibility/gestures/GestureManifold.java @@ -19,6 +19,7 @@ package com.android.server.accessibility.gestures; import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_DOUBLE_TAP; import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_DOUBLE_TAP_AND_HOLD; import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SINGLE_TAP; +import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SINGLE_TAP_AND_HOLD; import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SWIPE_DOWN; import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SWIPE_LEFT; import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SWIPE_RIGHT; @@ -27,11 +28,13 @@ import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_DOUBLE_TAP; import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_DOUBLE_TAP_AND_HOLD; import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_SINGLE_TAP; +import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_SINGLE_TAP_AND_HOLD; import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_SWIPE_DOWN; import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_SWIPE_LEFT; import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_SWIPE_RIGHT; import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_SWIPE_UP; import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_TRIPLE_TAP; +import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_TRIPLE_TAP_AND_HOLD; import static android.accessibilityservice.AccessibilityService.GESTURE_4_FINGER_DOUBLE_TAP; import static android.accessibilityservice.AccessibilityService.GESTURE_4_FINGER_DOUBLE_TAP_AND_HOLD; import static android.accessibilityservice.AccessibilityService.GESTURE_4_FINGER_SINGLE_TAP; @@ -96,11 +99,15 @@ class GestureManifold implements GestureMatcher.StateChangeListener { boolean mMultiFingerGesturesEnabled; // Whether the two-finger passthrough is enabled when multi-finger gestures are enabled. private boolean mTwoFingerPassthroughEnabled; + // Whether to send the motion events during gesture dispatch. + private boolean mSendMotionEventsEnabled = false; // A list of all the multi-finger gestures, for easy adding and removal. private final List<GestureMatcher> mMultiFingerGestures = new ArrayList<>(); // A list of two-finger swipes, for easy adding and removal when turning on or off two-finger // passthrough. private final List<GestureMatcher> mTwoFingerSwipes = new ArrayList<>(); + // The list of motion events for the current gesture. + private List<MotionEvent> mEvents = new ArrayList<>(); // Shared state information. private TouchState mState; @@ -140,6 +147,9 @@ class GestureManifold implements GestureMatcher.StateChangeListener { mMultiFingerGestures.add( new MultiFingerMultiTap(mContext, 2, 1, GESTURE_2_FINGER_SINGLE_TAP, this)); mMultiFingerGestures.add( + new MultiFingerMultiTapAndHold( + mContext, 2, 1, GESTURE_2_FINGER_SINGLE_TAP_AND_HOLD, this)); + mMultiFingerGestures.add( new MultiFingerMultiTap(mContext, 2, 2, GESTURE_2_FINGER_DOUBLE_TAP, this)); mMultiFingerGestures.add( new MultiFingerMultiTapAndHold( @@ -153,9 +163,17 @@ class GestureManifold implements GestureMatcher.StateChangeListener { new MultiFingerMultiTap(mContext, 3, 2, GESTURE_3_FINGER_DOUBLE_TAP, this)); mMultiFingerGestures.add( new MultiFingerMultiTapAndHold( + mContext, 3, 1, GESTURE_3_FINGER_SINGLE_TAP_AND_HOLD, this)); + mMultiFingerGestures.add( + new MultiFingerMultiTapAndHold( mContext, 3, 2, GESTURE_3_FINGER_DOUBLE_TAP_AND_HOLD, this)); mMultiFingerGestures.add( new MultiFingerMultiTap(mContext, 3, 3, GESTURE_3_FINGER_TRIPLE_TAP, this)); + mMultiFingerGestures.add( + new MultiFingerMultiTapAndHold( + mContext, 3, 3, GESTURE_3_FINGER_TRIPLE_TAP_AND_HOLD, this)); + mMultiFingerGestures.add( + new MultiFingerMultiTap(mContext, 3, 3, GESTURE_3_FINGER_TRIPLE_TAP, this)); // Four-finger taps. mMultiFingerGestures.add( new MultiFingerMultiTap(mContext, 4, 1, GESTURE_4_FINGER_SINGLE_TAP, this)); @@ -216,6 +234,9 @@ class GestureManifold implements GestureMatcher.StateChangeListener { return false; } } + if (mSendMotionEventsEnabled) { + mEvents.add(MotionEvent.obtainNoHistory(rawEvent)); + } for (GestureMatcher matcher : mGestures) { if (matcher.getState() != GestureMatcher.STATE_GESTURE_CANCELED) { if (DEBUG) { @@ -226,9 +247,8 @@ class GestureManifold implements GestureMatcher.StateChangeListener { Slog.d(LOG_TAG, matcher.toString()); } if (matcher.getState() == GestureMatcher.STATE_GESTURE_COMPLETED) { - // Here we just clear and return. The actual gesture dispatch is done in + // Here we just return. The actual gesture dispatch is done in // onStateChanged(). - clear(); // No need to process this event any further. return true; } @@ -241,6 +261,11 @@ class GestureManifold implements GestureMatcher.StateChangeListener { for (GestureMatcher matcher : mGestures) { matcher.clear(); } + if (mEvents != null) { + while (mEvents.size() > 0) { + mEvents.remove(0).recycle(); + } + } } /** @@ -326,29 +351,28 @@ class GestureManifold implements GestureMatcher.StateChangeListener { case GESTURE_DOUBLE_TAP: if (mServiceHandlesDoubleTap) { AccessibilityGestureEvent gestureEvent = - new AccessibilityGestureEvent(gestureId, event.getDisplayId()); + new AccessibilityGestureEvent(gestureId, event.getDisplayId(), mEvents); mListener.onGestureCompleted(gestureEvent); } else { mListener.onDoubleTap(event, rawEvent, policyFlags); } - clear(); break; case GESTURE_DOUBLE_TAP_AND_HOLD: if (mServiceHandlesDoubleTap) { AccessibilityGestureEvent gestureEvent = - new AccessibilityGestureEvent(gestureId, event.getDisplayId()); + new AccessibilityGestureEvent(gestureId, event.getDisplayId(), mEvents); mListener.onGestureCompleted(gestureEvent); } else { mListener.onDoubleTapAndHold(event, rawEvent, policyFlags); } - clear(); break; default: AccessibilityGestureEvent gestureEvent = - new AccessibilityGestureEvent(gestureId, event.getDisplayId()); + new AccessibilityGestureEvent(gestureId, event.getDisplayId(), mEvents); mListener.onGestureCompleted(gestureEvent); break; } + clear(); } public boolean isMultiFingerGesturesEnabled() { @@ -392,4 +416,25 @@ class GestureManifold implements GestureMatcher.StateChangeListener { public boolean isServiceHandlesDoubleTapEnabled() { return mServiceHandlesDoubleTap; } + + public void setSendMotionEventsEnabled(boolean mode) { + mSendMotionEventsEnabled = mode; + if (!mode) { + while (mEvents.size() > 0) { + mEvents.remove(0).recycle(); + } + } + } + + public boolean isSendMotionEventsEnabled() { + return mSendMotionEventsEnabled; + } + + /** + * Returns the current list of motion events. It is the caller's responsibility to copy the list + * if they want it to persist after a call to clear(). + */ + public List<MotionEvent> getMotionEvents() { + return mEvents; + } } diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerMultiTap.java b/services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerMultiTap.java index e15c4951d2a2..46b46288cf84 100644 --- a/services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerMultiTap.java +++ b/services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerMultiTap.java @@ -162,6 +162,7 @@ class MultiFingerMultiTap extends GestureMatcher { // Accept down only before target number of fingers are down // or the finger count is not more than target. if ((currentFingerCount > mTargetFingerCount) || mIsTargetFingerCountReached) { + mIsTargetFingerCountReached = false; cancelGesture(event, rawEvent, policyFlags); return; } diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java index d8c692b88a0f..5460e80a2802 100644 --- a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java +++ b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java @@ -37,6 +37,7 @@ import static android.view.accessibility.AccessibilityEvent.TYPE_VIEW_HOVER_EXIT import static com.android.server.accessibility.gestures.TouchState.ALL_POINTER_ID_BITS; import android.accessibilityservice.AccessibilityGestureEvent; +import android.accessibilityservice.AccessibilityService; import android.annotation.NonNull; import android.content.Context; import android.graphics.Region; @@ -340,6 +341,14 @@ public class TouchExplorer extends BaseEventStreamTransformation public void onDoubleTapAndHold(MotionEvent event, MotionEvent rawEvent, int policyFlags) { if (mDispatcher.longPressWithTouchEvents(event, policyFlags)) { sendHoverExitAndTouchExplorationGestureEndIfNeeded(policyFlags); + if (isSendMotionEventsEnabled()) { + AccessibilityGestureEvent gestureEvent = + new AccessibilityGestureEvent( + AccessibilityService.GESTURE_DOUBLE_TAP_AND_HOLD, + event.getDisplayId(), + mGestureDetector.getMotionEvents()); + mAms.onGesture(gestureEvent); + } mState.startDelegating(); } } @@ -350,7 +359,14 @@ public class TouchExplorer extends BaseEventStreamTransformation // Remove pending event deliveries. mSendHoverEnterAndMoveDelayed.cancel(); mSendHoverExitDelayed.cancel(); - + if (isSendMotionEventsEnabled()) { + AccessibilityGestureEvent gestureEvent = + new AccessibilityGestureEvent( + AccessibilityService.GESTURE_DOUBLE_TAP, + event.getDisplayId(), + mGestureDetector.getMotionEvents()); + mAms.onGesture(gestureEvent); + } if (mSendTouchExplorationEndDelayed.isPending()) { mSendTouchExplorationEndDelayed.forceSendAndRemove(); } @@ -385,6 +401,9 @@ public class TouchExplorer extends BaseEventStreamTransformation @Override public boolean onGestureCompleted(AccessibilityGestureEvent gestureEvent) { + if (DEBUG) { + Slog.d(LOG_TAG, "Dispatching gesture event:" + gestureEvent.toString()); + } endGestureDetection(true); mSendTouchInteractionEndDelayed.cancel(); mAms.onGesture(gestureEvent); @@ -411,12 +430,24 @@ public class TouchExplorer extends BaseEventStreamTransformation mDispatcher.sendMotionEvent( event, ACTION_HOVER_MOVE, - mState.getLastReceivedEvent(), + event, pointerIdBits, policyFlags); return true; } } + if (isSendMotionEventsEnabled()) { + // Send a gesture with motion events to represent the cancelled gesture. + AccessibilityGestureEvent gestureEvent = + new AccessibilityGestureEvent( + AccessibilityService.GESTURE_UNKNOWN, + event.getDisplayId(), + mGestureDetector.getMotionEvents()); + if (DEBUG) { + Slog.d(LOG_TAG, "Dispatching gesture event:" + gestureEvent.toString()); + } + mAms.onGesture(gestureEvent); + } return false; } @@ -620,6 +651,14 @@ public class TouchExplorer extends BaseEventStreamTransformation if (isDraggingGesture(event)) { // Two pointers moving in the same direction within // a given distance perform a drag. + if (isSendMotionEventsEnabled()) { + AccessibilityGestureEvent gestureEvent = + new AccessibilityGestureEvent( + AccessibilityService.GESTURE_PASSTHROUGH, + event.getDisplayId(), + mGestureDetector.getMotionEvents()); + mAms.onGesture(gestureEvent); + } computeDraggingPointerIdIfNeeded(event); pointerIdBits = 1 << mDraggingPointerId; event.setEdgeFlags(mReceivedPointerTracker.getLastReceivedDownEdgeFlags()); @@ -636,6 +675,14 @@ public class TouchExplorer extends BaseEventStreamTransformation mState.startDragging(); } else { // Two pointers moving arbitrary are delegated to the view hierarchy. + if (isSendMotionEventsEnabled()) { + AccessibilityGestureEvent gestureEvent = + new AccessibilityGestureEvent( + AccessibilityService.GESTURE_PASSTHROUGH, + event.getDisplayId(), + mGestureDetector.getMotionEvents()); + mAms.onGesture(gestureEvent); + } mState.startDelegating(); mDispatcher.sendDownForAllNotInjectedPointers(event, policyFlags); } @@ -650,6 +697,14 @@ public class TouchExplorer extends BaseEventStreamTransformation if (DEBUG) { Slog.d(LOG_TAG, "Three-finger edge swipe detected."); } + if (isSendMotionEventsEnabled()) { + AccessibilityGestureEvent gestureEvent = + new AccessibilityGestureEvent( + AccessibilityService.GESTURE_PASSTHROUGH, + event.getDisplayId(), + mGestureDetector.getMotionEvents()); + mAms.onGesture(gestureEvent); + } mState.startDelegating(); if (mState.isTouchExploring()) { mDispatcher.sendDownForAllNotInjectedPointers(event, @@ -663,6 +718,14 @@ public class TouchExplorer extends BaseEventStreamTransformation } } else { // More than two pointers are delegated to the view hierarchy. + if (isSendMotionEventsEnabled()) { + AccessibilityGestureEvent gestureEvent = + new AccessibilityGestureEvent( + AccessibilityService.GESTURE_PASSTHROUGH, + event.getDisplayId(), + mGestureDetector.getMotionEvents()); + mAms.onGesture(gestureEvent); + } mState.startDelegating(); event = MotionEvent.obtainNoHistory(event); mDispatcher.sendDownForAllNotInjectedPointers(event, policyFlags); @@ -1109,6 +1172,7 @@ public class TouchExplorer extends BaseEventStreamTransformation public void setTwoFingerPassthroughEnabled(boolean enabled) { mGestureDetector.setTwoFingerPassthroughEnabled(enabled); } + public void setGestureDetectionPassthroughRegion(Region region) { mGestureDetectionPassthroughRegion = region; } @@ -1117,6 +1181,17 @@ public class TouchExplorer extends BaseEventStreamTransformation mTouchExplorationPassthroughRegion = region; } + /** + * Whether to send the motion events that make up each gesture to the accessibility service. + */ + public void setSendMotionEventsEnabled(boolean mode) { + mGestureDetector.setSendMotionEventsEnabled(mode); + } + + public boolean isSendMotionEventsEnabled() { + return mGestureDetector.isSendMotionEventsEnabled(); + } + private boolean shouldPerformGestureDetection(MotionEvent event) { if (mState.isDelegating()) { return false; @@ -1213,7 +1288,14 @@ public class TouchExplorer extends BaseEventStreamTransformation public void run() { // Send an accessibility event to announce the touch exploration start. mDispatcher.sendAccessibilityEvent(TYPE_TOUCH_EXPLORATION_GESTURE_START); - + if (isSendMotionEventsEnabled()) { + AccessibilityGestureEvent gestureEvent = + new AccessibilityGestureEvent( + AccessibilityService.GESTURE_TOUCH_EXPLORATION, + mState.getLastReceivedEvent().getDisplayId(), + mGestureDetector.getMotionEvents()); + mAms.onGesture(gestureEvent); + } if (!mEvents.isEmpty() && !mRawEvents.isEmpty()) { // Deliver a down event. mDispatcher.sendMotionEvent(mEvents.get(0), ACTION_HOVER_ENTER, diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java index 35312a3a2fac..1c4d752c9775 100644 --- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java +++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java @@ -664,8 +664,12 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku if (targetWidget != null && targetWidget != widget) continue; PendingIntent intent = null; if (onClickIntent != null) { + // Rare informational activity click is okay being + // immutable; the tradeoff is more security in exchange for + // losing bounds-based window animations intent = PendingIntent.getActivity(mContext, widget.appWidgetId, - onClickIntent, PendingIntent.FLAG_UPDATE_CURRENT); + onClickIntent, + PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE); } RemoteViews views = createMaskedWidgetRemoteViews(iconBitmap, showBadge, intent); if (widget.replaceWithMaskedViewsLocked(views)) { @@ -2409,8 +2413,10 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku intent.setComponent(provider.info.provider); final long token = Binder.clearCallingIdentity(); try { + // Broadcast alarms sent by system are immutable provider.broadcast = PendingIntent.getBroadcastAsUser(mContext, 1, intent, - PendingIntent.FLAG_UPDATE_CURRENT, provider.info.getProfile()); + PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE, + provider.info.getProfile()); } finally { Binder.restoreCallingIdentity(token); } diff --git a/services/autofill/java/com/android/server/autofill/AutofillInlineSessionController.java b/services/autofill/java/com/android/server/autofill/AutofillInlineSessionController.java index c25dd37bc7d9..dd5e58a94d15 100644 --- a/services/autofill/java/com/android/server/autofill/AutofillInlineSessionController.java +++ b/services/autofill/java/com/android/server/autofill/AutofillInlineSessionController.java @@ -35,8 +35,8 @@ import java.util.function.Consumer; * Controls the interaction with the IME for the {@link AutofillInlineSuggestionsRequestSession}s. * * <p>The class maintains the inline suggestion session with the autofill service. There is at most - * one active inline suggestion session at any given corresponding to one focused view. - * New sessions are created only when {@link #onCreateInlineSuggestionsRequestLocked} is called.</p> + * one active inline suggestion session at any given corresponding to one focused view. New + * sessions are created only when {@link #onCreateInlineSuggestionsRequestLocked} is called.</p> * * <p>The class manages the interaction between the {@link com.android.server.autofill.Session} and * the inline suggestion session whenever inline suggestions can be provided. All calls to the @@ -83,7 +83,6 @@ final class AutofillInlineSessionController { @GuardedBy("mLock") void onCreateInlineSuggestionsRequestLocked(@NonNull AutofillId autofillId, @NonNull Consumer<InlineSuggestionsRequest> requestConsumer, @NonNull Bundle uiExtras) { - // TODO(b/151123764): rename the method to better reflect what it does. if (mSession != null) { // Destroy the existing session. mSession.destroySessionLocked(); diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java index d59c955de889..864ead1485b5 100644 --- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java +++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java @@ -55,6 +55,7 @@ import android.service.autofill.FieldClassification; import android.service.autofill.FieldClassification.Match; import android.service.autofill.FillEventHistory; import android.service.autofill.FillEventHistory.Event; +import android.service.autofill.FillEventHistory.Event.NoSaveReason; import android.service.autofill.FillResponse; import android.service.autofill.IAutoFillService; import android.service.autofill.InlineSuggestionRenderService; @@ -429,9 +430,15 @@ final class AutofillManagerServiceImpl return; } - session.logContextCommitted(); + final Session.SaveResult saveResult = session.showSaveLocked(); - final boolean finished = session.showSaveLocked(); + session.logContextCommitted(saveResult.getNoSaveReason()); + + if (saveResult.isLogSaveShown()) { + session.logSaveUiShown(); + } + + final boolean finished = saveResult.isRemoveSession(); if (sVerbose) Slog.v(TAG, "finishSessionLocked(): session finished on save? " + finished); if (finished) { @@ -868,7 +875,9 @@ final class AutofillManagerServiceImpl @NonNull ComponentName appComponentName, boolean compatMode) { logContextCommittedLocked(sessionId, clientState, selectedDatasets, ignoredDatasets, changedFieldIds, changedDatasetIds, manuallyFilledFieldIds, - manuallyFilledDatasetIds, null, null, appComponentName, compatMode); + manuallyFilledDatasetIds, /* detectedFieldIdsList= */ null, + /* detectedFieldClassificationsList= */ null, appComponentName, compatMode, + Event.NO_SAVE_REASON_NONE); } @GuardedBy("mLock") @@ -881,7 +890,8 @@ final class AutofillManagerServiceImpl @Nullable ArrayList<ArrayList<String>> manuallyFilledDatasetIds, @Nullable ArrayList<AutofillId> detectedFieldIdsList, @Nullable ArrayList<FieldClassification> detectedFieldClassificationsList, - @NonNull ComponentName appComponentName, boolean compatMode) { + @NonNull ComponentName appComponentName, boolean compatMode, + @NoSaveReason int saveDialogNotShowReason) { if (isValidEventLocked("logDatasetNotSelected()", sessionId)) { if (sVerbose) { Slog.v(TAG, "logContextCommitted() with FieldClassification: id=" + sessionId @@ -893,7 +903,8 @@ final class AutofillManagerServiceImpl + ", detectedFieldIds=" + detectedFieldIdsList + ", detectedFieldClassifications=" + detectedFieldClassificationsList + ", appComponentName=" + appComponentName.toShortString() - + ", compatMode=" + compatMode); + + ", compatMode=" + compatMode + + ", saveDialogNotShowReason=" + saveDialogNotShowReason); } AutofillId[] detectedFieldsIds = null; FieldClassification[] detectedFieldClassifications = null; @@ -929,7 +940,7 @@ final class AutofillManagerServiceImpl clientState, selectedDatasets, ignoredDatasets, changedFieldIds, changedDatasetIds, manuallyFilledFieldIds, manuallyFilledDatasetIds, - detectedFieldsIds, detectedFieldClassifications)); + detectedFieldsIds, detectedFieldClassifications, saveDialogNotShowReason)); } } @@ -1044,7 +1055,7 @@ final class AutofillManagerServiceImpl pw.println(compatPkgs); } pw.print(prefix); pw.print("Inline Suggestions Enabled: "); - pw.println(isInlineSuggestionsEnabled()); + pw.println(isInlineSuggestionsEnabledLocked()); pw.print(prefix); pw.print("Last prune: "); pw.println(mLastPrune); mDisabledInfoCache.dump(mUserId, prefix, pw); @@ -1157,7 +1168,7 @@ final class AutofillManagerServiceImpl } @GuardedBy("mLock") - boolean isInlineSuggestionsEnabled() { + boolean isInlineSuggestionsEnabledLocked() { if (mInfo != null) { return mInfo.isInlineSuggestionsEnabled(); } diff --git a/services/autofill/java/com/android/server/autofill/Helper.java b/services/autofill/java/com/android/server/autofill/Helper.java index 80b0375a229d..e35c0ee4e59b 100644 --- a/services/autofill/java/com/android/server/autofill/Helper.java +++ b/services/autofill/java/com/android/server/autofill/Helper.java @@ -24,6 +24,9 @@ import android.app.assist.AssistStructure.WindowNode; import android.content.ComponentName; import android.metrics.LogMaker; import android.service.autofill.Dataset; +import android.service.autofill.InternalSanitizer; +import android.service.autofill.SaveInfo; +import android.text.TextUtils; import android.util.ArrayMap; import android.util.ArraySet; import android.util.Slog; @@ -38,6 +41,7 @@ import com.android.internal.util.ArrayUtils; import java.io.PrintWriter; import java.util.ArrayDeque; import java.util.ArrayList; +import java.util.Arrays; public final class Helper { @@ -234,6 +238,46 @@ public final class Helper { } } + @Nullable + static ArrayMap<AutofillId, InternalSanitizer> createSanitizers(@Nullable SaveInfo saveInfo) { + if (saveInfo == null) return null; + + final InternalSanitizer[] sanitizerKeys = saveInfo.getSanitizerKeys(); + if (sanitizerKeys == null) return null; + + final int size = sanitizerKeys.length; + final ArrayMap<AutofillId, InternalSanitizer> sanitizers = new ArrayMap<>(size); + if (sDebug) Slog.d(TAG, "Service provided " + size + " sanitizers"); + final AutofillId[][] sanitizerValues = saveInfo.getSanitizerValues(); + for (int i = 0; i < size; i++) { + final InternalSanitizer sanitizer = sanitizerKeys[i]; + final AutofillId[] ids = sanitizerValues[i]; + if (sDebug) { + Slog.d(TAG, "sanitizer #" + i + " (" + sanitizer + ") for ids " + + Arrays.toString(ids)); + } + for (AutofillId id : ids) { + sanitizers.put(id, sanitizer); + } + } + return sanitizers; + } + + /** + * Returns true if {@code s1} contains all characters of {@code s2}, in order. + */ + static boolean containsCharsInOrder(String s1, String s2) { + int prevIndex = -1; + for (char ch : s2.toCharArray()) { + int index = TextUtils.indexOf(s1, ch, prevIndex + 1); + if (index == -1) { + return false; + } + prevIndex = index; + } + return true; + } + private interface ViewNodeFilter { boolean matches(ViewNode node); } diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java index 4d2e4f6ee486..f596b072d713 100644 --- a/services/autofill/java/com/android/server/autofill/Session.java +++ b/services/autofill/java/com/android/server/autofill/Session.java @@ -30,6 +30,8 @@ import static android.view.autofill.AutofillManager.FLAG_SMART_SUGGESTION_SYSTEM import static android.view.autofill.AutofillManager.getSmartSuggestionModeToString; import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage; +import static com.android.server.autofill.Helper.containsCharsInOrder; +import static com.android.server.autofill.Helper.createSanitizers; import static com.android.server.autofill.Helper.getNumericValue; import static com.android.server.autofill.Helper.sDebug; import static com.android.server.autofill.Helper.sVerbose; @@ -71,6 +73,8 @@ import android.service.autofill.FieldClassification; import android.service.autofill.FieldClassification.Match; import android.service.autofill.FieldClassificationUserData; import android.service.autofill.FillContext; +import android.service.autofill.FillEventHistory.Event; +import android.service.autofill.FillEventHistory.Event.NoSaveReason; import android.service.autofill.FillRequest; import android.service.autofill.FillResponse; import android.service.autofill.InlinePresentation; @@ -115,7 +119,6 @@ import java.util.Collections; import java.util.List; import java.util.Objects; import java.util.Optional; -import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Consumer; import java.util.function.Function; @@ -370,26 +373,24 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState * CountDownLatch. */ private final class AssistDataReceiverImpl extends IAssistDataReceiver.Stub { - + @GuardedBy("mLock") + private boolean mWaitForInlineRequest; @GuardedBy("mLock") private InlineSuggestionsRequest mPendingInlineSuggestionsRequest; @GuardedBy("mLock") private FillRequest mPendingFillRequest; - @GuardedBy("mLock") - private CountDownLatch mCountDownLatch = new CountDownLatch(0); @Nullable Consumer<InlineSuggestionsRequest> newAutofillRequestLocked(ViewState viewState, boolean isInlineRequest) { - mCountDownLatch = new CountDownLatch(isInlineRequest ? 2 : 1); mPendingFillRequest = null; + mWaitForInlineRequest = isInlineRequest; mPendingInlineSuggestionsRequest = null; return isInlineRequest ? (inlineSuggestionsRequest) -> { synchronized (mLock) { - if (mCountDownLatch.getCount() == 0) { + if (!mWaitForInlineRequest || mPendingInlineSuggestionsRequest != null) { return; } mPendingInlineSuggestionsRequest = inlineSuggestionsRequest; - mCountDownLatch.countDown(); maybeRequestFillLocked(); viewState.resetState(ViewState.STATE_PENDING_CREATE_INLINE_REQUEST); } @@ -397,16 +398,23 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } void maybeRequestFillLocked() { - if (mCountDownLatch.getCount() > 0 || mPendingFillRequest == null) { + if (mPendingFillRequest == null) { return; } - if (mPendingInlineSuggestionsRequest != null) { + + if (mWaitForInlineRequest) { + if (mPendingInlineSuggestionsRequest == null) { + return; + } + mPendingFillRequest = new FillRequest(mPendingFillRequest.getId(), mPendingFillRequest.getFillContexts(), mPendingFillRequest.getClientState(), mPendingFillRequest.getFlags(), mPendingInlineSuggestionsRequest); } + mRemoteFillService.onFillRequest(mPendingFillRequest); mPendingInlineSuggestionsRequest = null; + mWaitForInlineRequest = false; mPendingFillRequest = null; } @@ -507,15 +515,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState request = new FillRequest(requestId, contexts, mClientState, flags, /*inlineSuggestionsRequest=*/null); - if (mCountDownLatch.getCount() > 0) { - mPendingFillRequest = request; - mCountDownLatch.countDown(); - maybeRequestFillLocked(); - } else { - // TODO(b/151867668): ideally this case should not happen, but it was - // observed, we should figure out why and fix. - mRemoteFillService.onFillRequest(request); - } + mPendingFillRequest = request; + maybeRequestFillLocked(); } if (mActivityToken != null) { @@ -543,6 +544,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState return ids; } + /** + * Returns the String value of an {@link AutofillValue} by {@link AutofillId id} if it is of + * type {@code AUTOFILL_TYPE_TEXT} or {@code AUTOFILL_TYPE_LIST}. + */ @Override @Nullable public String findByAutofillId(@NonNull AutofillId id) { @@ -704,7 +709,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState * Autofill provider). */ private boolean isInlineSuggestionsEnabledByAutofillProviderLocked() { - return mService.isInlineSuggestionsEnabled(); + return mService.isInlineSuggestionsEnabledLocked(); } private boolean isViewFocusedLocked(int flags) { @@ -765,7 +770,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState // triggers a new partition and we end up with many duplicate partitions. This is // enhanced as the focus change can be much faster than the taking of the assist structure. // Hence remove the currently queued request and replace it with the one queued after the - // structure is taken. This causes only one fill request per bust of focus changes. + // structure is taken. This causes only one fill request per burst of focus changes. cancelCurrentRequestLocked(); // Only ask IME to create inline suggestions request if Autofill provider supports it and @@ -778,7 +783,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState && isViewFocusedLocked(flags)) { Consumer<InlineSuggestionsRequest> inlineSuggestionsRequestConsumer = mAssistReceiver.newAutofillRequestLocked(viewState, - /*isInlineRequest=*/ true); + /* isInlineRequest= */ true); if (inlineSuggestionsRequestConsumer != null) { final AutofillId focusedId = mCurrentViewId; remoteRenderService.getInlineSuggestionsRendererInfo( @@ -792,8 +797,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState viewState.setState(ViewState.STATE_PENDING_CREATE_INLINE_REQUEST); } } else { - mAssistReceiver.newAutofillRequestLocked(viewState, - /*isInlineRequest=*/ false); + mAssistReceiver.newAutofillRequestLocked(viewState, /* isInlineRequest= */ false); } // Now request the assist structure data. @@ -1170,7 +1174,14 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState return null; } - // FillServiceCallbacks + // VultureCallback + @Override + public void onServiceDied(@NonNull RemoteFillService service) { + Slog.w(TAG, "removing session because service died"); + forceRemoveSelfLocked(); + } + + // AutoFillUiCallback @Override public void authenticate(int requestId, int datasetIndex, IntentSender intent, Bundle extras, boolean authenticateInline) { @@ -1200,13 +1211,6 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState this, authenticationId, intent, fillInIntent, authenticateInline)); } - // VultureCallback - @Override - public void onServiceDied(@NonNull RemoteFillService service) { - Slog.w(TAG, "removing session because service died"); - forceRemoveSelfLocked(); - } - // AutoFillUiCallback @Override public void fill(int requestId, int datasetIndex, Dataset dataset) { @@ -1280,6 +1284,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } } + // AutoFillUiCallback @Override public void dispatchUnhandledKey(AutofillId id, KeyEvent keyEvent) { synchronized (mLock) { @@ -1596,10 +1601,22 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState * when necessary. */ public void logContextCommitted() { - mHandler.sendMessage(obtainMessage(Session::handleLogContextCommitted, this)); + mHandler.sendMessage(obtainMessage(Session::handleLogContextCommitted, this, + Event.NO_SAVE_REASON_NONE)); } - private void handleLogContextCommitted() { + /** + * Generates a {@link android.service.autofill.FillEventHistory.Event#TYPE_CONTEXT_COMMITTED} + * when necessary. + * + * @param saveDialogNotShowReason The reason why a save dialog was not shown. + */ + public void logContextCommitted(@NoSaveReason int saveDialogNotShowReason) { + mHandler.sendMessage(obtainMessage(Session::handleLogContextCommitted, this, + saveDialogNotShowReason)); + } + + private void handleLogContextCommitted(@NoSaveReason int saveDialogNotShowReason) { final FillResponse lastResponse; synchronized (mLock) { lastResponse = getLastResponseLocked("logContextCommited(%s)"); @@ -1629,22 +1646,25 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState // Sets field classification scores if (userData != null && fcStrategy != null) { - logFieldClassificationScore(fcStrategy, userData); + logFieldClassificationScore(fcStrategy, userData, saveDialogNotShowReason); } else { - logContextCommitted(null, null); + logContextCommitted(null, null, saveDialogNotShowReason); } } private void logContextCommitted(@Nullable ArrayList<AutofillId> detectedFieldIds, - @Nullable ArrayList<FieldClassification> detectedFieldClassifications) { + @Nullable ArrayList<FieldClassification> detectedFieldClassifications, + @NoSaveReason int saveDialogNotShowReason) { synchronized (mLock) { - logContextCommittedLocked(detectedFieldIds, detectedFieldClassifications); + logContextCommittedLocked(detectedFieldIds, detectedFieldClassifications, + saveDialogNotShowReason); } } @GuardedBy("mLock") private void logContextCommittedLocked(@Nullable ArrayList<AutofillId> detectedFieldIds, - @Nullable ArrayList<FieldClassification> detectedFieldClassifications) { + @Nullable ArrayList<FieldClassification> detectedFieldClassifications, + @NoSaveReason int saveDialogNotShowReason) { final FillResponse lastResponse = getLastResponseLocked("logContextCommited(%s)"); if (lastResponse == null) return; @@ -1822,10 +1842,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } } - mService.logContextCommittedLocked(id, mClientState, mSelectedDatasetIds, - ignoredDatasets, changedFieldIds, changedDatasetIds, - manuallyFilledFieldIds, manuallyFilledDatasetIds, detectedFieldIds, - detectedFieldClassifications, mComponentName, mCompatMode); + mService.logContextCommittedLocked(id, mClientState, mSelectedDatasetIds, ignoredDatasets, + changedFieldIds, changedDatasetIds, manuallyFilledFieldIds, + manuallyFilledDatasetIds, detectedFieldIds, detectedFieldClassifications, + mComponentName, mCompatMode, saveDialogNotShowReason); } /** @@ -1833,7 +1853,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState * {@code fieldId} based on its {@code currentValue} and {@code userData}. */ private void logFieldClassificationScore(@NonNull FieldClassificationStrategy fcStrategy, - @NonNull FieldClassificationUserData userData) { + @NonNull FieldClassificationUserData userData, + @NoSaveReason int saveDialogNotShowReason) { final String[] userValues = userData.getValues(); final String[] categoryIds = userData.getCategoryIds(); @@ -1879,7 +1900,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState final RemoteCallback callback = new RemoteCallback((result) -> { if (result == null) { if (sDebug) Slog.d(TAG, "setFieldClassificationScore(): no results"); - logContextCommitted(null, null); + logContextCommitted(null, null, saveDialogNotShowReason); return; } final Scores scores = result.getParcelable(EXTRA_SCORES); @@ -1940,7 +1961,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState return; } - logContextCommitted(detectedFieldIds, detectedFieldClassifications); + logContextCommitted(detectedFieldIds, detectedFieldClassifications, + saveDialogNotShowReason); }); fcStrategy.calculateScores(callback, currentValues, userValues, categoryIds, @@ -1948,17 +1970,28 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } /** + * Generates a {@link android.service.autofill.FillEventHistory.Event#TYPE_SAVE_SHOWN} + * when necessary. + * + * <p>Note: It is necessary to call logContextCommitted() first before calling this method. + */ + public void logSaveUiShown() { + mHandler.sendMessage(obtainMessage(Session::logSaveShown, this)); + } + + /** * Shows the save UI, when session can be saved. * - * @return {@code true} if session is done and could be removed, or {@code false} if it's - * pending user action or the service asked to keep it alive (for multi-screens workflow). + * @return {@link SaveResult} that contains the save ui display status information. */ @GuardedBy("mLock") - public boolean showSaveLocked() { + @NonNull + public SaveResult showSaveLocked() { if (mDestroyed) { Slog.w(TAG, "Call to Session#showSaveLocked() rejected - session: " + id + " destroyed"); - return false; + return new SaveResult(/* logSaveShown= */ false, /* removeSession= */ false, + Event.NO_SAVE_REASON_NONE); } final FillResponse response = getLastResponseLocked("showSaveLocked(%s)"); final SaveInfo saveInfo = response == null ? null : response.getSaveInfo(); @@ -1975,13 +2008,15 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState */ if (saveInfo == null) { if (sVerbose) Slog.v(TAG, "showSaveLocked(" + this.id + "): no saveInfo from service"); - return true; + return new SaveResult(/* logSaveShown= */ false, /* removeSession= */ true, + Event.NO_SAVE_REASON_NO_SAVE_INFO); } if ((saveInfo.getFlags() & SaveInfo.FLAG_DELAY_SAVE) != 0) { // TODO(b/113281366): log metrics if (sDebug) Slog.v(TAG, "showSaveLocked(" + this.id + "): service asked to delay save"); - return false; + return new SaveResult(/* logSaveShown= */ false, /* removeSession= */ false, + Event.NO_SAVE_REASON_WITH_DELAY_SAVE_FLAG); } final ArrayMap<AutofillId, InternalSanitizer> sanitizers = createSanitizers(saveInfo); @@ -2073,7 +2108,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState Slog.v(TAG, "allRequiredAreNotEmpty: " + allRequiredAreNotEmpty + " hasOptional: " + (optionalIds != null)); } - if (allRequiredAreNotEmpty) { + int saveDialogNotShowReason; + if (!allRequiredAreNotEmpty) { + saveDialogNotShowReason = Event.NO_SAVE_REASON_HAS_EMPTY_REQUIRED; + } else { // Must look up all optional ids in 2 scenarios: // - if no required id changed but an optional id did, it should trigger save / update // - if at least one required id changed but it was not part of a filled dataset, we @@ -2124,7 +2162,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } } } - if (atLeastOneChanged) { + if (!atLeastOneChanged) { + saveDialogNotShowReason = Event.NO_SAVE_REASON_NO_VALUE_CHANGED; + } else { if (sDebug) { Slog.d(TAG, "at least one field changed, validate fields for save UI"); } @@ -2142,13 +2182,15 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState Slog.e(TAG, "Not showing save UI because validation failed:", e); log.setType(MetricsEvent.TYPE_FAILURE); mMetricsLogger.write(log); - return true; + return new SaveResult(/* logSaveShown= */ false, /* removeSession= */ true, + Event.NO_SAVE_REASON_FIELD_VALIDATION_FAILED); } mMetricsLogger.write(log); if (!isValid) { Slog.i(TAG, "not showing save UI because fields failed validation"); - return true; + return new SaveResult(/* logSaveShown= */ false, /* removeSession= */ true, + Event.NO_SAVE_REASON_FIELD_VALIDATION_FAILED); } } @@ -2187,7 +2229,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState Slog.d(TAG, "ignoring Save UI because all fields match contents of " + "dataset #" + i + ": " + dataset); } - return true; + return new SaveResult(/* logSaveShown= */ false, /* removeSession= */ true, + Event.NO_SAVE_REASON_DATASET_MATCH); } } @@ -2196,9 +2239,6 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState + id + "!"); } - // Use handler so logContextCommitted() is logged first - mHandler.sendMessage(obtainMessage(Session::logSaveShown, this)); - final IAutoFillManagerClient client = getClient(); mPendingSaveUi = new PendingUi(new Binder(), id, client); @@ -2210,8 +2250,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } if (serviceLabel == null || serviceIcon == null) { wtf(null, "showSaveLocked(): no service label or icon"); - return true; + return new SaveResult(/* logSaveShown= */ false, /* removeSession= */ true, + Event.NO_SAVE_REASON_NONE); } + getUiForShowing().showSaveUi(serviceLabel, serviceIcon, mService.getServicePackageName(), saveInfo, this, mComponentName, this, mPendingSaveUi, isUpdate, mCompatMode); @@ -2223,7 +2265,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } } mIsSaving = true; - return false; + return new SaveResult(/* logSaveShown= */ true, /* removeSession= */ false, + Event.NO_SAVE_REASON_NONE); } } // Nothing changed... @@ -2232,7 +2275,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState + "allRequiredAreNotNull=" + allRequiredAreNotEmpty + ", atLeastOneChanged=" + atLeastOneChanged); } - return true; + return new SaveResult(/* logSaveShown= */ false, /* removeSession= */ true, + saveDialogNotShowReason); } private void logSaveShown() { @@ -2240,31 +2284,6 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } @Nullable - private ArrayMap<AutofillId, InternalSanitizer> createSanitizers(@Nullable SaveInfo saveInfo) { - if (saveInfo == null) return null; - - final InternalSanitizer[] sanitizerKeys = saveInfo.getSanitizerKeys(); - if (sanitizerKeys == null) return null; - - final int size = sanitizerKeys.length ; - final ArrayMap<AutofillId, InternalSanitizer> sanitizers = new ArrayMap<>(size); - if (sDebug) Slog.d(TAG, "Service provided " + size + " sanitizers"); - final AutofillId[][] sanitizerValues = saveInfo.getSanitizerValues(); - for (int i = 0; i < size; i++) { - final InternalSanitizer sanitizer = sanitizerKeys[i]; - final AutofillId[] ids = sanitizerValues[i]; - if (sDebug) { - Slog.d(TAG, "sanitizer #" + i + " (" + sanitizer + ") for ids " - + Arrays.toString(ids)); - } - for (AutofillId id : ids) { - sanitizers.put(id, sanitizer); - } - } - return sanitizers; - } - - @Nullable private AutofillValue getSanitizedValue( @Nullable ArrayMap<AutofillId, InternalSanitizer> sanitizers, @NonNull AutofillId id, @@ -2328,12 +2347,12 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState */ @GuardedBy("mLock") @Nullable - private CharSequence[] getAutofillOptionsFromContextsLocked(AutofillId id) { + private CharSequence[] getAutofillOptionsFromContextsLocked(@NonNull AutofillId autofillId) { final int numContexts = mContexts.size(); - for (int i = numContexts - 1; i >= 0; i--) { final FillContext context = mContexts.get(i); - final ViewNode node = Helper.findViewNodeByAutofillId(context.getStructure(), id); + final ViewNode node = Helper.findViewNodeByAutofillId(context.getStructure(), + autofillId); if (node != null && node.getAutofillOptions() != null) { return node.getAutofillOptions(); } @@ -2439,7 +2458,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState // TODO(b/113281366): rather than merge it here, it might be better to simply reuse the old // session instead of creating a new one. But we need to consider what would happen on corner // cases such as "Main Activity M -> activity A with username -> activity B with password" - // If user follows the normal workflow, than session A would be merged with session B as + // If user follows the normal workflow, then session A would be merged with session B as // expected. But if when on Activity A the user taps back or somehow launches another activity, // session A could be merged with the wrong session. /** @@ -2532,11 +2551,11 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } requestNewFillResponseLocked(viewState, ViewState.STATE_STARTED_PARTITION, flags); return true; - } else { - if (sVerbose) { - Slog.v(TAG, "Not starting new partition for view " + id + ": " - + viewState.getStateAsString()); - } + } + + if (sVerbose) { + Slog.v(TAG, "Not starting new partition for view " + id + ": " + + viewState.getStateAsString()); } return false; } @@ -2897,21 +2916,6 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } } - /** - * Returns true if {@code s1} contains all characters of {@code s2}, in order. - */ - private static boolean containsCharsInOrder(String s1, String s2) { - int prevIndex = -1; - for (char ch : s2.toCharArray()) { - int index = TextUtils.indexOf(s1, ch, prevIndex + 1); - if (index == -1) { - return false; - } - prevIndex = index; - } - return true; - } - @Override public void onFillReady(@NonNull FillResponse response, @NonNull AutofillId filledId, @Nullable AutofillValue value) { @@ -3380,7 +3384,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState mInlineSessionController.getInlineSuggestionsRequestLocked().orElse(null)); } if (mAugmentedAutofillDestroyer == null) { - mAugmentedAutofillDestroyer = () -> remoteService.onDestroyAutofillWindowsRequest(); + mAugmentedAutofillDestroyer = remoteService::onDestroyAutofillWindowsRequest; } return mAugmentedAutofillDestroyer; } @@ -3608,6 +3612,97 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } } + /** + * The result of checking whether to show the save dialog, when session can be saved. + * + * @hide + */ + static final class SaveResult { + /** + * Whether to record the save dialog has been shown. + */ + private boolean mLogSaveShown; + + /** + * Whether to remove the session. + */ + private boolean mRemoveSession; + + /** + * The reason why a save dialog was not shown. + */ + @NoSaveReason private int mSaveDialogNotShowReason; + + SaveResult(boolean logSaveShown, boolean removeSession, + @NoSaveReason int saveDialogNotShowReason) { + mLogSaveShown = logSaveShown; + mRemoveSession = removeSession; + mSaveDialogNotShowReason = saveDialogNotShowReason; + } + + /** + * Returns whether to record the save dialog has been shown. + * + * @return Whether to record the save dialog has been shown. + */ + public boolean isLogSaveShown() { + return mLogSaveShown; + } + + /** + * Sets whether to record the save dialog has been shown. + * + * @param logSaveShown Whether to record the save dialog has been shown. + */ + public void setLogSaveShown(boolean logSaveShown) { + mLogSaveShown = logSaveShown; + } + + /** + * Returns whether to remove the session. + * + * @return Whether to remove the session. + */ + public boolean isRemoveSession() { + return mRemoveSession; + } + + /** + * Sets whether to remove the session. + * + * @param removeSession Whether to remove the session. + */ + public void setRemoveSession(boolean removeSession) { + mRemoveSession = removeSession; + } + + /** + * Returns the reason why a save dialog was not shown. + * + * @return The reason why a save dialog was not shown. + */ + @NoSaveReason + public int getNoSaveReason() { + return mSaveDialogNotShowReason; + } + + /** + * Sets the reason why a save dialog was not shown. + * + * @param saveDialogNotShowReason The reason why a save dialog was not shown. + */ + public void setSaveDialogNotShowReason(@NoSaveReason int saveDialogNotShowReason) { + mSaveDialogNotShowReason = saveDialogNotShowReason; + } + + @Override + public String toString() { + return "SaveResult: [logSaveShown=" + mLogSaveShown + + ", removeSession=" + mRemoveSession + + ", saveDialogNotShowReason=" + mSaveDialogNotShowReason + "]"; + } + } + @Override public String toString() { return "Session: [id=" + id + ", component=" + mComponentName + "]"; diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerService.java b/services/backup/java/com/android/server/backup/UserBackupManagerService.java index aa8532b67c43..2ff66b564ec9 100644 --- a/services/backup/java/com/android/server/backup/UserBackupManagerService.java +++ b/services/backup/java/com/android/server/backup/UserBackupManagerService.java @@ -650,7 +650,7 @@ public class UserBackupManagerService { context, /* requestCode */ 0, initIntent, - /* flags */ 0, + /* flags */ PendingIntent.FLAG_IMMUTABLE, UserHandle.of(userId)); // Set up the backup-request journaling diff --git a/services/core/Android.bp b/services/core/Android.bp index 26c28d5c95b1..1a7f0d1ce37e 100644 --- a/services/core/Android.bp +++ b/services/core/Android.bp @@ -1,6 +1,7 @@ filegroup { name: "services.core-sources", srcs: ["java/**/*.java"], + exclude_srcs: [":connectivity-service-srcs"], path: "java", visibility: [ "//frameworks/base/services", @@ -122,6 +123,7 @@ java_library_static { "netd_aidl_interfaces-platform-java", "overlayable_policy_aidl-java", "SurfaceFlingerProperties", + "com.android.sysprop.watchdog", ], } @@ -166,3 +168,50 @@ prebuilt_etc { name: "protolog.conf.json.gz", src: ":services.core.json.gz", } + +// TODO: Move connectivity service sources to independent directory. +filegroup { + name: "connectivity-service-srcs", + srcs: [ + "java/com/android/server/ConnectivityService.java", + "java/com/android/server/ConnectivityServiceInitializer.java", + "java/com/android/server/TestNetworkService.java", + "java/com/android/server/connectivity/AutodestructReference.java", + "java/com/android/server/connectivity/ConnectivityConstants.java", + "java/com/android/server/connectivity/DataConnectionStats.java", + "java/com/android/server/connectivity/DefaultNetworkMetrics.java", + "java/com/android/server/connectivity/DnsManager.java", + "java/com/android/server/connectivity/IpConnectivityEventBuilder.java", + "java/com/android/server/connectivity/IpConnectivityMetrics.java", + "java/com/android/server/connectivity/KeepaliveTracker.java", + "java/com/android/server/connectivity/LingerMonitor.java", + "java/com/android/server/connectivity/MockableSystemProperties.java", + "java/com/android/server/connectivity/MultipathPolicyTracker.java", + "java/com/android/server/connectivity/Nat464Xlat.java", + "java/com/android/server/connectivity/NetdEventListenerService.java", + "java/com/android/server/connectivity/NetworkAgentInfo.java", + "java/com/android/server/connectivity/NetworkDiagnostics.java", + "java/com/android/server/connectivity/NetworkNotificationManager.java", + "java/com/android/server/connectivity/NetworkRanker.java", + "java/com/android/server/connectivity/PermissionMonitor.java", + "java/com/android/server/connectivity/ProxyTracker.java", + "java/com/android/server/connectivity/TcpKeepaliveController.java", + "java/com/android/server/connectivity/Vpn.java", + "java/com/android/server/connectivity/VpnIkev2Utils.java", + "java/com/android/server/net/LockdownVpnTracker.java", + ], +} + +java_library { + name: "service-connectivity", + srcs: [ + ":connectivity-service-srcs", + ], + installable: true, + libs: [ + "android.net.ipsec.ike", + "services.core", + "services.net", + "unsupportedappusage", + ], +} diff --git a/services/core/java/com/android/server/BluetoothAirplaneModeListener.java b/services/core/java/com/android/server/BluetoothAirplaneModeListener.java index 31cd5d519d87..4d9680c785bc 100644 --- a/services/core/java/com/android/server/BluetoothAirplaneModeListener.java +++ b/services/core/java/com/android/server/BluetoothAirplaneModeListener.java @@ -16,22 +16,14 @@ package com.android.server; -import android.bluetooth.BluetoothA2dp; -import android.bluetooth.BluetoothAdapter; -import android.bluetooth.BluetoothHearingAid; -import android.bluetooth.BluetoothProfile; -import android.bluetooth.BluetoothProfile.ServiceListener; import android.content.Context; -import android.content.res.Resources; import android.database.ContentObserver; import android.os.Handler; import android.os.Looper; import android.os.Message; import android.provider.Settings; import android.util.Log; -import android.widget.Toast; -import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; /** @@ -53,7 +45,7 @@ class BluetoothAirplaneModeListener { private final BluetoothManagerService mBluetoothManager; private final BluetoothAirplaneModeHandler mHandler; - private AirplaneModeHelper mAirplaneHelper; + private BluetoothModeChangeHelper mAirplaneHelper; @VisibleForTesting int mToastCount = 0; @@ -97,7 +89,7 @@ class BluetoothAirplaneModeListener { * Call after boot complete */ @VisibleForTesting - void start(AirplaneModeHelper helper) { + void start(BluetoothModeChangeHelper helper) { Log.i(TAG, "start"); mAirplaneHelper = helper; mToastCount = mAirplaneHelper.getSettingsInt(TOAST_COUNT); @@ -141,118 +133,4 @@ class BluetoothAirplaneModeListener { } return true; } - - /** - * Helper class that handles callout and callback methods without - * complex logic. - */ - @VisibleForTesting - public static class AirplaneModeHelper { - private volatile BluetoothA2dp mA2dp; - private volatile BluetoothHearingAid mHearingAid; - private final BluetoothAdapter mAdapter; - private final Context mContext; - - AirplaneModeHelper(Context context) { - mAdapter = BluetoothAdapter.getDefaultAdapter(); - mContext = context; - - mAdapter.getProfileProxy(mContext, mProfileServiceListener, BluetoothProfile.A2DP); - mAdapter.getProfileProxy(mContext, mProfileServiceListener, - BluetoothProfile.HEARING_AID); - } - - private final ServiceListener mProfileServiceListener = new ServiceListener() { - @Override - public void onServiceConnected(int profile, BluetoothProfile proxy) { - // Setup Bluetooth profile proxies - switch (profile) { - case BluetoothProfile.A2DP: - mA2dp = (BluetoothA2dp) proxy; - break; - case BluetoothProfile.HEARING_AID: - mHearingAid = (BluetoothHearingAid) proxy; - break; - default: - break; - } - } - - @Override - public void onServiceDisconnected(int profile) { - // Clear Bluetooth profile proxies - switch (profile) { - case BluetoothProfile.A2DP: - mA2dp = null; - break; - case BluetoothProfile.HEARING_AID: - mHearingAid = null; - break; - default: - break; - } - } - }; - - @VisibleForTesting - public boolean isA2dpOrHearingAidConnected() { - return isA2dpConnected() || isHearingAidConnected(); - } - - @VisibleForTesting - public boolean isBluetoothOn() { - final BluetoothAdapter adapter = mAdapter; - if (adapter == null) { - return false; - } - return adapter.getLeState() == BluetoothAdapter.STATE_ON; - } - - @VisibleForTesting - public boolean isAirplaneModeOn() { - return Settings.Global.getInt(mContext.getContentResolver(), - Settings.Global.AIRPLANE_MODE_ON, 0) == 1; - } - - @VisibleForTesting - public void onAirplaneModeChanged(BluetoothManagerService managerService) { - managerService.onAirplaneModeChanged(); - } - - @VisibleForTesting - public int getSettingsInt(String name) { - return Settings.Global.getInt(mContext.getContentResolver(), - name, 0); - } - - @VisibleForTesting - public void setSettingsInt(String name, int value) { - Settings.Global.putInt(mContext.getContentResolver(), - name, value); - } - - @VisibleForTesting - public void showToastMessage() { - Resources r = mContext.getResources(); - final CharSequence text = r.getString( - R.string.bluetooth_airplane_mode_toast, 0); - Toast.makeText(mContext, text, Toast.LENGTH_LONG).show(); - } - - private boolean isA2dpConnected() { - final BluetoothA2dp a2dp = mA2dp; - if (a2dp == null) { - return false; - } - return a2dp.getConnectedDevices().size() > 0; - } - - private boolean isHearingAidConnected() { - final BluetoothHearingAid hearingAid = mHearingAid; - if (hearingAid == null) { - return false; - } - return hearingAid.getConnectedDevices().size() > 0; - } - }; } diff --git a/services/core/java/com/android/server/BluetoothDeviceConfigListener.java b/services/core/java/com/android/server/BluetoothDeviceConfigListener.java new file mode 100644 index 000000000000..2dcf82ff9410 --- /dev/null +++ b/services/core/java/com/android/server/BluetoothDeviceConfigListener.java @@ -0,0 +1,64 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server; + +import android.provider.DeviceConfig; + +/** + * The BluetoothDeviceConfigListener handles system device config change callback and checks + * whether we need to inform BluetoothManagerService on this change. + * + * The information of device config change would not be passed to the BluetoothManagerService + * when Bluetooth is on and Bluetooth is in one of the following situations: + * 1. Bluetooth A2DP is connected. + * 2. Bluetooth Hearing Aid profile is connected. + */ +class BluetoothDeviceConfigListener { + private static final String TAG = "BluetoothDeviceConfigListener"; + + BluetoothManagerService mService; + + BluetoothDeviceConfigListener(BluetoothManagerService service) { + mService = service; + DeviceConfig.addOnPropertiesChangedListener( + DeviceConfig.NAMESPACE_BLUETOOTH, + (Runnable r) -> r.run(), + mDeviceConfigChangedListener); + } + + private final DeviceConfig.OnPropertiesChangedListener mDeviceConfigChangedListener = + new DeviceConfig.OnPropertiesChangedListener() { + @Override + public void onPropertiesChanged(DeviceConfig.Properties properties) { + if (!properties.getNamespace().equals(DeviceConfig.NAMESPACE_BLUETOOTH)) { + return; + } + boolean foundInit = false; + for (String name : properties.getKeyset()) { + if (name.startsWith("INIT_")) { + foundInit = true; + break; + } + } + if (!foundInit) { + return; + } + mService.onInitFlagsChanged(); + } + }; + +} diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java index 011231c016e2..f6a29aa917f2 100644 --- a/services/core/java/com/android/server/BluetoothManagerService.java +++ b/services/core/java/com/android/server/BluetoothManagerService.java @@ -23,7 +23,9 @@ import android.Manifest; import android.app.ActivityManager; import android.app.AppGlobals; import android.app.AppOpsManager; +import android.bluetooth.BluetoothA2dp; import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothHearingAid; import android.bluetooth.BluetoothProfile; import android.bluetooth.BluetoothProtoEnums; import android.bluetooth.IBluetooth; @@ -63,7 +65,6 @@ import android.os.UserHandle; import android.os.UserManager; import android.os.UserManagerInternal; import android.os.UserManagerInternal.UserRestrictionsListener; -import android.provider.DeviceConfig; import android.provider.Settings; import android.provider.Settings.SettingNotFoundException; import android.text.TextUtils; @@ -118,6 +119,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { // Delay for retrying enable and disable in msec private static final int ENABLE_DISABLE_DELAY_MS = 300; private static final int DELAY_BEFORE_RESTART_DUE_TO_INIT_FLAGS_CHANGED_MS = 300; + private static final int DELAY_FOR_RETRY_INIT_FLAG_CHECK_MS = 86400; private static final int MESSAGE_ENABLE = 1; private static final int MESSAGE_DISABLE = 2; @@ -178,8 +180,12 @@ class BluetoothManagerService extends IBluetoothManager.Stub { private int mWaitForEnableRetry; private int mWaitForDisableRetry; + private BluetoothModeChangeHelper mBluetoothModeChangeHelper; + private BluetoothAirplaneModeListener mBluetoothAirplaneModeListener; + private BluetoothDeviceConfigListener mBluetoothDeviceConfigListener; + // used inside handler thread private boolean mQuietEnable = false; private boolean mEnable; @@ -284,29 +290,13 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } }; - private final DeviceConfig.OnPropertiesChangedListener mDeviceConfigChangedListener = - new DeviceConfig.OnPropertiesChangedListener() { - @Override - public void onPropertiesChanged(DeviceConfig.Properties properties) { - if (!properties.getNamespace().equals(DeviceConfig.NAMESPACE_BLUETOOTH)) { - return; - } - boolean foundInit = false; - for (String name : properties.getKeyset()) { - if (name.startsWith("INIT_")) { - foundInit = true; - break; - } - } - if (!foundInit) { - return; - } - mHandler.removeMessages(MESSAGE_INIT_FLAGS_CHANGED); - mHandler.sendEmptyMessageDelayed( - MESSAGE_INIT_FLAGS_CHANGED, - DELAY_BEFORE_RESTART_DUE_TO_INIT_FLAGS_CHANGED_MS); - } - }; + @VisibleForTesting + public void onInitFlagsChanged() { + mHandler.removeMessages(MESSAGE_INIT_FLAGS_CHANGED); + mHandler.sendEmptyMessageDelayed( + MESSAGE_INIT_FLAGS_CHANGED, + DELAY_BEFORE_RESTART_DUE_TO_INIT_FLAGS_CHANGED_MS); + } public boolean onFactoryReset() { mContext.enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, @@ -457,6 +447,15 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mHandler.sendMessage(msg); } } + } else if (BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED.equals(action) + || BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED.equals(action)) { + final int state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, + BluetoothProfile.STATE_CONNECTED); + if (mHandler.hasMessages(MESSAGE_INIT_FLAGS_CHANGED) + && state == BluetoothProfile.STATE_DISCONNECTED + && !mBluetoothModeChangeHelper.isA2dpOrHearingAidConnected()) { + onInitFlagsChanged(); + } } } }; @@ -508,6 +507,8 @@ class BluetoothManagerService extends IBluetoothManager.Stub { filter.addAction(BluetoothAdapter.ACTION_LOCAL_NAME_CHANGED); filter.addAction(BluetoothAdapter.ACTION_BLUETOOTH_ADDRESS_CHANGED); filter.addAction(Intent.ACTION_SETTING_RESTORED); + filter.addAction(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED); + filter.addAction(BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED); filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); mContext.registerReceiver(mReceiver, filter); @@ -542,10 +543,6 @@ class BluetoothManagerService extends IBluetoothManager.Stub { Slog.w(TAG, "Unable to resolve SystemUI's UID."); } mSystemUiUid = systemUiUid; - DeviceConfig.addOnPropertiesChangedListener( - DeviceConfig.NAMESPACE_BLUETOOTH, - (Runnable r) -> r.run(), - mDeviceConfigChangedListener); } /** @@ -1383,10 +1380,12 @@ class BluetoothManagerService extends IBluetoothManager.Stub { Message getMsg = mHandler.obtainMessage(MESSAGE_GET_NAME_AND_ADDRESS); mHandler.sendMessage(getMsg); } + + mBluetoothModeChangeHelper = new BluetoothModeChangeHelper(mContext); if (mBluetoothAirplaneModeListener != null) { - mBluetoothAirplaneModeListener.start( - new BluetoothAirplaneModeListener.AirplaneModeHelper(mContext)); + mBluetoothAirplaneModeListener.start(mBluetoothModeChangeHelper); } + mBluetoothDeviceConfigListener = new BluetoothDeviceConfigListener(this); } /** @@ -2229,6 +2228,12 @@ class BluetoothManagerService extends IBluetoothManager.Stub { Slog.d(TAG, "MESSAGE_INIT_FLAGS_CHANGED"); } mHandler.removeMessages(MESSAGE_INIT_FLAGS_CHANGED); + if (mBluetoothModeChangeHelper.isA2dpOrHearingAidConnected()) { + mHandler.sendEmptyMessageDelayed( + MESSAGE_INIT_FLAGS_CHANGED, + DELAY_FOR_RETRY_INIT_FLAG_CHECK_MS); + break; + } if (mBluetooth != null && isEnabled()) { restartForReason( BluetoothProtoEnums.ENABLE_DISABLE_REASON_INIT_FLAGS_CHANGED); diff --git a/services/core/java/com/android/server/BluetoothModeChangeHelper.java b/services/core/java/com/android/server/BluetoothModeChangeHelper.java new file mode 100644 index 000000000000..242fa848c25e --- /dev/null +++ b/services/core/java/com/android/server/BluetoothModeChangeHelper.java @@ -0,0 +1,143 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server; + +import android.bluetooth.BluetoothA2dp; +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothHearingAid; +import android.bluetooth.BluetoothProfile; +import android.bluetooth.BluetoothProfile.ServiceListener; +import android.content.Context; +import android.content.res.Resources; +import android.provider.Settings; +import android.widget.Toast; + +import com.android.internal.R; +import com.android.internal.annotations.VisibleForTesting; + +/** + * Helper class that handles callout and callback methods without + * complex logic. + */ +public class BluetoothModeChangeHelper { + private volatile BluetoothA2dp mA2dp; + private volatile BluetoothHearingAid mHearingAid; + private final BluetoothAdapter mAdapter; + private final Context mContext; + + BluetoothModeChangeHelper(Context context) { + mAdapter = BluetoothAdapter.getDefaultAdapter(); + mContext = context; + + mAdapter.getProfileProxy(mContext, mProfileServiceListener, BluetoothProfile.A2DP); + mAdapter.getProfileProxy(mContext, mProfileServiceListener, + BluetoothProfile.HEARING_AID); + } + + private final ServiceListener mProfileServiceListener = new ServiceListener() { + @Override + public void onServiceConnected(int profile, BluetoothProfile proxy) { + // Setup Bluetooth profile proxies + switch (profile) { + case BluetoothProfile.A2DP: + mA2dp = (BluetoothA2dp) proxy; + break; + case BluetoothProfile.HEARING_AID: + mHearingAid = (BluetoothHearingAid) proxy; + break; + default: + break; + } + } + + @Override + public void onServiceDisconnected(int profile) { + // Clear Bluetooth profile proxies + switch (profile) { + case BluetoothProfile.A2DP: + mA2dp = null; + break; + case BluetoothProfile.HEARING_AID: + mHearingAid = null; + break; + default: + break; + } + } + }; + + @VisibleForTesting + public boolean isA2dpOrHearingAidConnected() { + return isA2dpConnected() || isHearingAidConnected(); + } + + @VisibleForTesting + public boolean isBluetoothOn() { + final BluetoothAdapter adapter = mAdapter; + if (adapter == null) { + return false; + } + return adapter.getLeState() == BluetoothAdapter.STATE_ON; + } + + @VisibleForTesting + public boolean isAirplaneModeOn() { + return Settings.Global.getInt(mContext.getContentResolver(), + Settings.Global.AIRPLANE_MODE_ON, 0) == 1; + } + + @VisibleForTesting + public void onAirplaneModeChanged(BluetoothManagerService managerService) { + managerService.onAirplaneModeChanged(); + } + + @VisibleForTesting + public int getSettingsInt(String name) { + return Settings.Global.getInt(mContext.getContentResolver(), + name, 0); + } + + @VisibleForTesting + public void setSettingsInt(String name, int value) { + Settings.Global.putInt(mContext.getContentResolver(), + name, value); + } + + @VisibleForTesting + public void showToastMessage() { + Resources r = mContext.getResources(); + final CharSequence text = r.getString( + R.string.bluetooth_airplane_mode_toast, 0); + Toast.makeText(mContext, text, Toast.LENGTH_LONG).show(); + } + + private boolean isA2dpConnected() { + final BluetoothA2dp a2dp = mA2dp; + if (a2dp == null) { + return false; + } + return a2dp.getConnectedDevices().size() > 0; + } + + private boolean isHearingAidConnected() { + final BluetoothHearingAid hearingAid = mHearingAid; + if (hearingAid == null) { + return false; + } + return hearingAid.getConnectedDevices().size() > 0; + } +} diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index bb9f6d2c8354..0cd6e08101da 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -236,7 +236,6 @@ import java.net.UnknownHostException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; -import java.util.Collections; import java.util.Comparator; import java.util.ConcurrentModificationException; import java.util.HashMap; @@ -1170,7 +1169,7 @@ public class ConnectivityService extends IConnectivityManager.Stub mMultipathPolicyTracker = new MultipathPolicyTracker(mContext, mHandler); - mDnsManager = new DnsManager(mContext, mDnsResolver, mSystemProperties); + mDnsManager = new DnsManager(mContext, mDnsResolver); registerPrivateDnsSettingsCallbacks(); } @@ -2301,10 +2300,21 @@ public class ConnectivityService extends IConnectivityManager.Stub } /** - * Called when the system is ready and ConnectivityService can initialize remaining components. + * Called by SystemServer through ConnectivityManager when the system is ready. */ - @VisibleForTesting + @Override public void systemReady() { + if (Binder.getCallingUid() != Process.SYSTEM_UID) { + throw new SecurityException("Calling Uid is not system uid."); + } + systemReadyInternal(); + } + + /** + * Called when ConnectivityService can initialize remaining components. + */ + @VisibleForTesting + public void systemReadyInternal() { // Let PermissionMonitor#startMonitoring() running in the beginning of the systemReady // before MultipathPolicyTracker.start(). Since mApps in PermissionMonitor needs to be // populated first to ensure that listening network request which is sent by @@ -2461,12 +2471,11 @@ public class ConnectivityService extends IConnectivityManager.Stub loge("Can't set TCP buffer sizes:" + e); } - Integer rwndValue = Settings.Global.getInt(mContext.getContentResolver(), - Settings.Global.TCP_DEFAULT_INIT_RWND, + final Integer rwndValue = Settings.Global.getInt(mContext.getContentResolver(), + Settings.Global.TCP_DEFAULT_INIT_RWND, mSystemProperties.getInt("net.tcp.default_init_rwnd", 0)); - final String sysctlKey = "sys.sysctl.tcp_def_init_rwnd"; if (rwndValue != 0) { - mSystemProperties.set(sysctlKey, rwndValue.toString()); + mSystemProperties.setTcpInitRwnd(rwndValue); } } @@ -6192,20 +6201,12 @@ public class ConnectivityService extends IConnectivityManager.Stub return; // no updating necessary } - 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 { mDnsManager.noteDnsServersForNetwork(netId, newLp); - // 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) mDnsManager.setDefaultDnsSystemProperties(newLp.getDnsServers()); mDnsManager.flushVmDnsCache(); } catch (Exception e) { loge("Exception in setDnsConfigurationForNetwork: " + e); @@ -6720,8 +6721,6 @@ public class ConnectivityService extends IConnectivityManager.Stub ? newNetwork.linkProperties.getHttpProxy() : null); updateTcpBufferSizes(null != newNetwork ? newNetwork.linkProperties.getTcpBufferSizes() : null); - mDnsManager.setDefaultDnsSystemProperties(null != newNetwork - ? newNetwork.linkProperties.getDnsServers() : Collections.EMPTY_LIST); notifyIfacesChangedForNetworkStats(); // Fix up the NetworkCapabilities of any VPNs that don't specify underlying networks. updateAllVpnsCapabilities(); diff --git a/services/core/java/com/android/server/ConnectivityServiceInitializer.java b/services/core/java/com/android/server/ConnectivityServiceInitializer.java new file mode 100644 index 000000000000..2bc8925be019 --- /dev/null +++ b/services/core/java/com/android/server/ConnectivityServiceInitializer.java @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server; + +import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_HIGH; +import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_NORMAL; + +import android.content.Context; +import android.net.INetworkPolicyManager; +import android.net.INetworkStatsService; +import android.os.INetworkManagementService; +import android.os.ServiceManager; +import android.util.Log; + +/** + * Connectivity service initializer for core networking. This is called by system server to create + * a new instance of ConnectivityService. + */ +public final class ConnectivityServiceInitializer extends SystemService { + private static final String TAG = ConnectivityServiceInitializer.class.getSimpleName(); + private final ConnectivityService mConnectivity; + + public ConnectivityServiceInitializer(Context context) { + super(context); + // TODO: Define formal APIs to get the needed services. + mConnectivity = new ConnectivityService(context, getNetworkManagementService(), + getNetworkStatsService(), getNetworkPolicyManager()); + } + + @Override + public void onStart() { + Log.i(TAG, "Registering " + Context.CONNECTIVITY_SERVICE); + publishBinderService(Context.CONNECTIVITY_SERVICE, mConnectivity, + /* allowIsolated= */ false, DUMP_FLAG_PRIORITY_HIGH | DUMP_FLAG_PRIORITY_NORMAL); + } + + private INetworkManagementService getNetworkManagementService() { + return INetworkManagementService.Stub.asInterface( + ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE)); + } + + private INetworkStatsService getNetworkStatsService() { + return INetworkStatsService.Stub.asInterface( + ServiceManager.getService(Context.NETWORK_STATS_SERVICE)); + } + + private INetworkPolicyManager getNetworkPolicyManager() { + return INetworkPolicyManager.Stub.asInterface( + ServiceManager.getService(Context.NETWORK_POLICY_SERVICE)); + } + +} diff --git a/services/core/java/com/android/server/GestureLauncherService.java b/services/core/java/com/android/server/GestureLauncherService.java index b3d4085288dd..20f68da1592e 100644 --- a/services/core/java/com/android/server/GestureLauncherService.java +++ b/services/core/java/com/android/server/GestureLauncherService.java @@ -38,7 +38,6 @@ import android.os.SystemProperties; import android.os.Trace; import android.os.UserHandle; import android.provider.Settings; -import android.telecom.TelecomManager; import android.util.MutableBoolean; import android.util.Slog; import android.view.KeyEvent; @@ -46,7 +45,6 @@ import android.view.KeyEvent; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; -import com.android.server.LocalServices; import com.android.server.statusbar.StatusBarManagerInternal; import com.android.server.wm.WindowManagerInternal; @@ -430,11 +428,18 @@ public class GestureLauncherService extends SystemService { mPowerButtonConsecutiveTaps++; mPowerButtonSlowConsecutiveTaps++; } - if (mPanicButtonGestureEnabled - && mPowerButtonConsecutiveTaps == PANIC_POWER_TAP_COUNT_THRESHOLD) { - launchPanic = true; - intercept = interactive; - } else if (mCameraDoubleTapPowerEnabled + // Check if we need to launch camera or panic flows + if (mPanicButtonGestureEnabled) { + // Commit to intercepting the powerkey event after the second "quick" tap to avoid + // lockscreen changes between launching camera and the panic flow. + if (mPowerButtonConsecutiveTaps > 1) { + intercept = interactive; + } + if (mPowerButtonConsecutiveTaps == PANIC_POWER_TAP_COUNT_THRESHOLD) { + launchPanic = true; + } + } + if (mCameraDoubleTapPowerEnabled && powerTapInterval < CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS && mPowerButtonConsecutiveTaps == CAMERA_POWER_TAP_COUNT_THRESHOLD) { launchCamera = true; @@ -464,8 +469,11 @@ public class GestureLauncherService extends SystemService { mMetricsLogger.histogram("power_consecutive_short_tap_count", mPowerButtonSlowConsecutiveTaps); mMetricsLogger.histogram("power_double_tap_interval", (int) powerTapInterval); + outLaunched.value = launchCamera || launchPanic; - return intercept && (launchCamera || launchPanic); + // Intercept power key event if the press is part of a gesture (camera, panic) and the user + // has completed setup. + return intercept && isUserSetupComplete(); } /** @@ -475,8 +483,7 @@ public class GestureLauncherService extends SystemService { boolean handleCameraGesture(boolean useWakelock, int source) { Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "GestureLauncher:handleCameraGesture"); try { - boolean userSetupComplete = Settings.Secure.getIntForUser(mContext.getContentResolver(), - Settings.Secure.USER_SETUP_COMPLETE, 0, UserHandle.USER_CURRENT) != 0; + boolean userSetupComplete = isUserSetupComplete(); if (!userSetupComplete) { if (DBG) { Slog.d(TAG, String.format( @@ -514,8 +521,7 @@ public class GestureLauncherService extends SystemService { Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "GestureLauncher:handlePanicButtonGesture"); try { - boolean userSetupComplete = Settings.Secure.getIntForUser(mContext.getContentResolver(), - Settings.Secure.USER_SETUP_COMPLETE, 0, UserHandle.USER_CURRENT) != 0; + boolean userSetupComplete = isUserSetupComplete(); if (!userSetupComplete) { if (DBG) { Slog.d(TAG, String.format( @@ -529,21 +535,20 @@ public class GestureLauncherService extends SystemService { "userSetupComplete = %s, performing panic gesture.", userSetupComplete)); } - // TODO(b/160006048): Not all devices have telephony. Check system feature first. - TelecomManager telecomManager = (TelecomManager) mContext.getSystemService( - Context.TELECOM_SERVICE); - mContext.startActivity(telecomManager.createLaunchEmergencyDialerIntent(null).addFlags( - Intent.FLAG_ACTIVITY_NEW_TASK - | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS - | Intent.FLAG_ACTIVITY_SINGLE_TOP).putExtra( - "com.android.phone.EmergencyDialer.extra.ENTRY_TYPE", - 2)); // 2 maps to power button, forcing into fast emergency dialer experience. + StatusBarManagerInternal service = LocalServices.getService( + StatusBarManagerInternal.class); + service.onEmergencyActionLaunchGestureDetected(); return true; } finally { Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); } } + private boolean isUserSetupComplete() { + return Settings.Secure.getIntForUser(mContext.getContentResolver(), + Settings.Secure.USER_SETUP_COMPLETE, 0, UserHandle.USER_CURRENT) != 0; + } + private final BroadcastReceiver mUserReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { diff --git a/services/core/java/com/android/server/NetworkTimeUpdateService.java b/services/core/java/com/android/server/NetworkTimeUpdateService.java index c34dd984f865..ff2308c35b9f 100644 --- a/services/core/java/com/android/server/NetworkTimeUpdateService.java +++ b/services/core/java/com/android/server/NetworkTimeUpdateService.java @@ -103,7 +103,9 @@ public class NetworkTimeUpdateService extends Binder { mCM = mContext.getSystemService(ConnectivityManager.class); Intent pollIntent = new Intent(ACTION_POLL, null); - mPendingPollIntent = PendingIntent.getBroadcast(mContext, POLL_REQUEST, pollIntent, 0); + // Broadcast alarms sent by system are immutable + mPendingPollIntent = PendingIntent.getBroadcast(mContext, POLL_REQUEST, pollIntent, + PendingIntent.FLAG_IMMUTABLE); mPollingIntervalMs = mContext.getResources().getInteger( com.android.internal.R.integer.config_ntpPollingInterval); diff --git a/services/core/java/com/android/server/SystemServiceManager.java b/services/core/java/com/android/server/SystemServiceManager.java index b87468419430..ff2661b19b48 100644 --- a/services/core/java/com/android/server/SystemServiceManager.java +++ b/services/core/java/com/android/server/SystemServiceManager.java @@ -26,12 +26,14 @@ import android.os.SystemClock; import android.os.Trace; import android.os.UserManagerInternal; import android.util.ArrayMap; +import android.util.EventLog; import android.util.Slog; import android.util.SparseArray; import com.android.internal.annotations.GuardedBy; import com.android.internal.util.Preconditions; import com.android.server.SystemService.TargetUser; +import com.android.server.am.EventLogTags; import com.android.server.utils.TimingsTraceAndSlog; import dalvik.system.PathClassLoader; @@ -53,12 +55,14 @@ public final class SystemServiceManager { private static final int SERVICE_CALL_WARN_TIME_MS = 50; // Constants used on onUser(...) - private static final String START = "Start"; - private static final String UNLOCKING = "Unlocking"; - private static final String UNLOCKED = "Unlocked"; - private static final String SWITCH = "Switch"; - private static final String STOP = "Stop"; - private static final String CLEANUP = "Cleanup"; + // NOTE: do not change their values, as they're used on Trace calls and changes might break + // performance tests that rely on them. + private static final String USER_STARTING = "Start"; + private static final String USER_UNLOCKING = "Unlocking"; + private static final String USER_UNLOCKED = "Unlocked"; + private static final String USER_SWITCHING = "Switch"; + private static final String USER_STOPPING = "Stop"; + private static final String USER_STOPPED = "Cleanup"; private static File sSystemDir; private final Context mContext; @@ -86,7 +90,7 @@ public final class SystemServiceManager { /** * Reference to the current user, it's used to set the {@link TargetUser} on - * {@link #switchUser(int, int)} as the previous user might have been removed already. + * {@link #onUserSwitching(int, int)} as the previous user might have been removed already. */ @GuardedBy("mTargetUsers") private @Nullable TargetUser mCurrentUser; @@ -275,33 +279,38 @@ public final class SystemServiceManager { /** * Starts the given user. */ - public void startUser(@NonNull TimingsTraceAndSlog t, @UserIdInt int userId) { + public void onUserStarting(@NonNull TimingsTraceAndSlog t, @UserIdInt int userId) { + EventLog.writeEvent(EventLogTags.SSM_USER_STARTING, userId); + final TargetUser targetUser = newTargetUser(userId); synchronized (mTargetUsers) { mTargetUsers.put(userId, targetUser); } - onUser(t, START, /* prevUser= */ null, targetUser); + onUser(t, USER_STARTING, /* prevUser= */ null, targetUser); } /** * Unlocks the given user. */ - public void unlockUser(@UserIdInt int userId) { - onUser(UNLOCKING, userId); + public void onUserUnlocking(@UserIdInt int userId) { + EventLog.writeEvent(EventLogTags.SSM_USER_UNLOCKING, userId); + onUser(USER_UNLOCKING, userId); } /** * Called after the user was unlocked. */ public void onUserUnlocked(@UserIdInt int userId) { - onUser(UNLOCKED, userId); + EventLog.writeEvent(EventLogTags.SSM_USER_UNLOCKED, userId); + onUser(USER_UNLOCKED, userId); } /** * Switches to the given user. */ - public void switchUser(@UserIdInt int from, @UserIdInt int to) { + public void onUserSwitching(@UserIdInt int from, @UserIdInt int to) { + EventLog.writeEvent(EventLogTags.SSM_USER_SWITCHING, from, to); final TargetUser curUser, prevUser; synchronized (mTargetUsers) { if (mCurrentUser == null) { @@ -321,21 +330,23 @@ public final class SystemServiceManager { Slog.d(TAG, "Set mCurrentUser to " + mCurrentUser); } } - onUser(TimingsTraceAndSlog.newAsyncLog(), SWITCH, prevUser, curUser); + onUser(TimingsTraceAndSlog.newAsyncLog(), USER_SWITCHING, prevUser, curUser); } /** * Stops the given user. */ - public void stopUser(@UserIdInt int userId) { - onUser(STOP, userId); + public void onUserStopping(@UserIdInt int userId) { + EventLog.writeEvent(EventLogTags.SSM_USER_STOPPING, userId); + onUser(USER_STOPPING, userId); } /** * Cleans up the given user. */ - public void cleanupUser(@UserIdInt int userId) { - onUser(CLEANUP, userId); + public void onUserStopped(@UserIdInt int userId) { + EventLog.writeEvent(EventLogTags.SSM_USER_STOPPED, userId); + onUser(USER_STOPPED, userId); // Remove cached TargetUser synchronized (mTargetUsers) { @@ -351,6 +362,7 @@ public final class SystemServiceManager { private void onUser(@NonNull TimingsTraceAndSlog t, @NonNull String onWhat, @Nullable TargetUser prevUser, @NonNull TargetUser curUser) { final int curUserId = curUser.getUserIdentifier(); + // NOTE: do not change label below, or it might break performance tests that rely on it. t.traceBegin("ssm." + onWhat + "User-" + curUserId); Slog.i(TAG, "Calling on" + onWhat + "User " + curUserId + (prevUser != null ? " (from " + prevUser + ")" : "")); @@ -381,22 +393,22 @@ public final class SystemServiceManager { long time = SystemClock.elapsedRealtime(); try { switch (onWhat) { - case SWITCH: + case USER_SWITCHING: service.onUserSwitching(prevUser, curUser); break; - case START: + case USER_STARTING: service.onUserStarting(curUser); break; - case UNLOCKING: + case USER_UNLOCKING: service.onUserUnlocking(curUser); break; - case UNLOCKED: + case USER_UNLOCKED: service.onUserUnlocked(curUser); break; - case STOP: + case USER_STOPPING: service.onUserStopping(curUser); break; - case CLEANUP: + case USER_STOPPED: service.onUserStopped(curUser); break; default: diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java index 990a547144a0..ba1eda99db10 100644 --- a/services/core/java/com/android/server/Watchdog.java +++ b/services/core/java/com/android/server/Watchdog.java @@ -23,7 +23,9 @@ import android.content.Intent; import android.content.IntentFilter; import android.hidl.manager.V1_0.IServiceManager; import android.os.Binder; +import android.os.Build; import android.os.Debug; +import android.os.FileUtils; import android.os.Handler; import android.os.IPowerManager; import android.os.Looper; @@ -31,10 +33,12 @@ import android.os.Process; import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemClock; +import android.os.SystemProperties; import android.util.EventLog; import android.util.Log; import android.util.Slog; import android.util.SparseArray; +import android.sysprop.WatchdogProperties; import com.android.internal.os.ProcessCpuTracker; import com.android.internal.os.ZygoteConnectionConstants; @@ -42,12 +46,16 @@ import com.android.internal.util.FrameworkStatsLog; import com.android.server.am.ActivityManagerService; import com.android.server.wm.SurfaceAnimationThread; +import java.io.BufferedReader; import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import java.io.StringWriter; import java.util.ArrayList; import java.util.Arrays; +import java.util.concurrent.TimeUnit; import java.util.HashSet; import java.util.List; @@ -75,6 +83,12 @@ public class Watchdog { private static final int WAITED_HALF = 2; private static final int OVERDUE = 3; + // Track watchdog timeout history and break the crash loop if there is. + private static final String TIMEOUT_HISTORY_FILE = "/data/system/watchdog-timeout-history.txt"; + private static final String PROP_FATAL_LOOP_COUNT = "framework_watchdog.fatal_count"; + private static final String PROP_FATAL_LOOP_WINDOWS_SECS = + "framework_watchdog.fatal_window.second"; + // Which native processes to dump into dropbox's stack traces public static final String[] NATIVE_STACKS_OF_INTEREST = new String[] { "/system/bin/audioserver", @@ -90,6 +104,7 @@ public class Watchdog { "media.metrics", // system/bin/mediametrics "media.codec", // vendor/bin/hw/android.hardware.media.omx@1.0-service "media.swcodec", // /apex/com.android.media.swcodec/bin/mediaswcodec + "media.transcoding", // Media transcoding service "com.android.bluetooth", // Bluetooth service "/apex/com.android.os.statsd/bin/statsd", // Stats daemon }; @@ -699,6 +714,10 @@ public class Watchdog { Slog.w(TAG, "*** WATCHDOG KILLING SYSTEM PROCESS: " + subject); WatchdogDiagnostics.diagnoseCheckers(blockedCheckers); Slog.w(TAG, "*** GOODBYE!"); + if (!Build.IS_USER && isCrashLoopFound() + && !WatchdogProperties.is_fatal_ignore().orElse(false)) { + breakCrashLoop(); + } Process.killProcess(Process.myPid()); System.exit(10); } @@ -716,4 +735,107 @@ public class Watchdog { Slog.w(TAG, "Failed to write to /proc/sysrq-trigger", e); } } + + private void resetTimeoutHistory() { + writeTimeoutHistory(new ArrayList<String>()); + } + + private void writeTimeoutHistory(Iterable<String> crashHistory) { + String data = String.join(",", crashHistory); + + try (FileWriter writer = new FileWriter(TIMEOUT_HISTORY_FILE)) { + writer.write(SystemProperties.get("ro.boottime.zygote")); + writer.write(":"); + writer.write(data); + } catch (IOException e) { + Slog.e(TAG, "Failed to write file " + TIMEOUT_HISTORY_FILE, e); + } + } + + private String[] readTimeoutHistory() { + final String[] emptyStringArray = {}; + + try (BufferedReader reader = new BufferedReader(new FileReader(TIMEOUT_HISTORY_FILE))) { + String line = reader.readLine(); + if (line == null) { + return emptyStringArray; + } + + String[] data = line.trim().split(":"); + String boottime = data.length >= 1 ? data[0] : ""; + String history = data.length >= 2 ? data[1] : ""; + if (SystemProperties.get("ro.boottime.zygote").equals(boottime) && !history.isEmpty()) { + return history.split(","); + } else { + return emptyStringArray; + } + } catch (FileNotFoundException e) { + return emptyStringArray; + } catch (IOException e) { + Slog.e(TAG, "Failed to read file " + TIMEOUT_HISTORY_FILE, e); + return emptyStringArray; + } + } + + private boolean hasActiveUsbConnection() { + try { + final String state = FileUtils.readTextFile( + new File("/sys/class/android_usb/android0/state"), + 128 /*max*/, null /*ellipsis*/).trim(); + if ("CONFIGURED".equals(state)) { + return true; + } + } catch (IOException e) { + Slog.w(TAG, "Failed to determine if device was on USB", e); + } + return false; + } + + private boolean isCrashLoopFound() { + int fatalCount = WatchdogProperties.fatal_count().orElse(0); + long fatalWindowMs = TimeUnit.SECONDS.toMillis( + WatchdogProperties.fatal_window_second().orElse(0)); + if (fatalCount == 0 || fatalWindowMs == 0) { + if (fatalCount != fatalWindowMs) { + Slog.w(TAG, String.format("sysprops '%s' and '%s' should be set or unset together", + PROP_FATAL_LOOP_COUNT, PROP_FATAL_LOOP_WINDOWS_SECS)); + } + return false; + } + + // new-history = [last (fatalCount - 1) items in old-history] + [nowMs]. + long nowMs = SystemClock.elapsedRealtime(); // Time since boot including deep sleep. + String[] rawCrashHistory = readTimeoutHistory(); + ArrayList<String> crashHistory = new ArrayList<String>(Arrays.asList(Arrays.copyOfRange( + rawCrashHistory, + Math.max(0, rawCrashHistory.length - fatalCount - 1), + rawCrashHistory.length))); + // Something wrong here. + crashHistory.add(String.valueOf(nowMs)); + writeTimeoutHistory(crashHistory); + + // Returns false if the device has an active USB connection. + if (hasActiveUsbConnection()) { + return false; + } + + long firstCrashMs; + try { + firstCrashMs = Long.parseLong(crashHistory.get(0)); + } catch (NumberFormatException t) { + Slog.w(TAG, "Failed to parseLong " + crashHistory.get(0), t); + resetTimeoutHistory(); + return false; + } + return crashHistory.size() >= fatalCount && nowMs - firstCrashMs < fatalWindowMs; + } + + private void breakCrashLoop() { + try (FileWriter kmsg = new FileWriter("/dev/kmsg_debug", /* append= */ true)) { + kmsg.append("Fatal reset to escape the system_server crashing loop\n"); + } catch (IOException e) { + Slog.w(TAG, "Failed to append to kmsg", e); + } + doSysRq('c'); + } } diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index 0f095ab8b85f..31712becec05 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -56,6 +56,8 @@ import android.app.Service; import android.app.ServiceStartArgs; import android.app.admin.DevicePolicyEventLogger; import android.appwidget.AppWidgetManagerInternal; +import android.compat.annotation.ChangeId; +import android.compat.annotation.Disabled; import android.content.ComponentName; import android.content.ComponentName.WithComponentName; import android.content.Context; @@ -79,6 +81,7 @@ import android.os.Message; import android.os.Process; import android.os.RemoteCallback; import android.os.RemoteException; +import android.os.ServiceManager; import android.os.SystemClock; import android.os.SystemProperties; import android.os.TransactionTooLargeException; @@ -100,6 +103,7 @@ import android.webkit.WebViewZygote; import com.android.internal.R; import com.android.internal.annotations.GuardedBy; import com.android.internal.app.procstats.ServiceState; +import com.android.internal.compat.IPlatformCompat; import com.android.internal.messages.nano.SystemMessageProto; import com.android.internal.notification.SystemNotificationChannels; import com.android.internal.os.BatteryStatsImpl; @@ -234,6 +238,16 @@ public final class ActiveServices { private static final SimpleDateFormat DATE_FORMATTER = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + private final IPlatformCompat mPlatformCompat; + + /** + * The BG-launch FGS restriction feature is going to be allowed only for apps targetSdkVersion + * is higher than R. + */ + @ChangeId + @Disabled + static final long FGS_BG_START_RESTRICTION_CHANGE_ID = 170668199L; + final Runnable mLastAnrDumpClearer = new Runnable() { @Override public void run() { synchronized (mAm) { @@ -430,6 +444,9 @@ public final class ActiveServices { } mMaxStartingBackground = maxBg > 0 ? maxBg : ActivityManager.isLowRamDeviceStatic() ? 1 : 8; + + final IBinder b = ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE); + mPlatformCompat = IPlatformCompat.Stub.asInterface(b); } void systemServicesReady() { @@ -557,7 +574,8 @@ public final class ActiveServices { r.mLoggedInfoAllowStartForeground = true; } if (r.mAllowStartForeground == FGS_FEATURE_DENIED - && mAm.mConstants.mFlagFgsStartRestrictionEnabled) { + && (mAm.mConstants.mFlagFgsStartRestrictionEnabled + || isChangeEnabled(FGS_BG_START_RESTRICTION_CHANGE_ID, r))) { if (mAm.mConstants.mFlagFgsStartTempAllowListEnabled && mAm.isOnDeviceIdleWhitelistLocked(r.appInfo.uid, false)) { // uid is on DeviceIdleController's allowlist. @@ -1468,7 +1486,8 @@ public final class ActiveServices { r.mLoggedInfoAllowStartForeground = true; } if (r.mAllowStartForeground == FGS_FEATURE_DENIED - && mAm.mConstants.mFlagFgsStartRestrictionEnabled) { + && (mAm.mConstants.mFlagFgsStartRestrictionEnabled + || isChangeEnabled(FGS_BG_START_RESTRICTION_CHANGE_ID, r))) { if (mAm.mConstants.mFlagFgsStartTempAllowListEnabled && mAm.isOnDeviceIdleWhitelistLocked(r.appInfo.uid, false)) { // uid is on DeviceIdleController's allowlist. @@ -5101,4 +5120,12 @@ public final class ActiveServices { context.getSystemService(NotificationManager.class).notifyAsUser(Long.toString(now), NOTE_FOREGROUND_SERVICE_BG_LAUNCH, n.build(), UserHandle.ALL); } + + private boolean isChangeEnabled(long changeId, ServiceRecord r) { + boolean enabled = false; + try { + enabled = mPlatformCompat.isChangeEnabled(changeId, r.appInfo); + } catch (RemoteException e) { } + return enabled; + } } diff --git a/services/core/java/com/android/server/am/ActiveUids.java b/services/core/java/com/android/server/am/ActiveUids.java index 4e1435e14679..c86c29f8ac2a 100644 --- a/services/core/java/com/android/server/am/ActiveUids.java +++ b/services/core/java/com/android/server/am/ActiveUids.java @@ -16,7 +16,12 @@ package com.android.server.am; +import android.app.ActivityManager; +import android.os.UserHandle; import android.util.SparseArray; +import android.util.proto.ProtoOutputStream; + +import java.io.PrintWriter; /** Class for tracking active uids for running processes. */ final class ActiveUids { @@ -71,4 +76,43 @@ final class ActiveUids { int indexOfKey(int uid) { return mActiveUids.indexOfKey(uid); } + + boolean dump(PrintWriter pw, String dumpPackage, int dumpAppId, + String header, boolean needSep) { + boolean printed = false; + for (int i = 0; i < mActiveUids.size(); i++) { + final UidRecord uidRec = mActiveUids.valueAt(i); + if (dumpPackage != null && UserHandle.getAppId(uidRec.uid) != dumpAppId) { + continue; + } + if (!printed) { + printed = true; + if (needSep) { + pw.println(); + } + pw.print(" "); pw.println(header); + } + pw.print(" UID "); UserHandle.formatUid(pw, uidRec.uid); + pw.print(": "); pw.println(uidRec); + pw.print(" curProcState="); pw.print(uidRec.mCurProcState); + pw.print(" curCapability="); + ActivityManager.printCapabilitiesFull(pw, uidRec.curCapability); + pw.println(); + for (int j = uidRec.procRecords.size() - 1; j >= 0; j--) { + pw.print(" proc="); + pw.println(uidRec.procRecords.valueAt(j)); + } + } + return printed; + } + + void dumpProto(ProtoOutputStream proto, String dumpPackage, int dumpAppId, long fieldId) { + for (int i = 0; i < mActiveUids.size(); i++) { + UidRecord uidRec = mActiveUids.valueAt(i); + if (dumpPackage != null && UserHandle.getAppId(uidRec.uid) != dumpAppId) { + continue; + } + uidRec.dumpDebug(proto, fieldId); + } + } } diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 9afda8c0fba4..63f7d44da1de 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -1979,7 +1979,6 @@ public class ActivityManagerService extends IActivityManager.Stub mConstants = hasHandlerThread ? new ActivityManagerConstants(mContext, this, mHandler) : null; final ActiveUids activeUids = new ActiveUids(this, false /* postChangesToAtm */); - mUidObserverController = new UidObserverController(this); mPlatformCompat = null; mProcessList = injector.getProcessList(this); mProcessList.init(this, activeUids, mPlatformCompat); @@ -1997,6 +1996,7 @@ public class ActivityManagerService extends IActivityManager.Stub mServices = hasHandlerThread ? new ActiveServices(this) : null; mSystemThread = null; mUiHandler = injector.getUiHandler(null /* service */); + mUidObserverController = new UidObserverController(mUiHandler); mUserController = hasHandlerThread ? new UserController(this) : null; mPendingIntentController = hasHandlerThread ? new PendingIntentController(handlerThread.getLooper(), mUserController, @@ -2077,7 +2077,7 @@ public class ActivityManagerService extends IActivityManager.Stub mCpHelper = new ContentProviderHelper(this, true); mPackageWatchdog = PackageWatchdog.getInstance(mUiContext); mAppErrors = new AppErrors(mUiContext, this, mPackageWatchdog); - mUidObserverController = new UidObserverController(this); + mUidObserverController = new UidObserverController(mUiHandler); final File systemDir = SystemServiceManager.ensureSystemDir(); @@ -7361,7 +7361,7 @@ public class ActivityManagerService extends IActivityManager.Stub final boolean bootingSystemUser = currentUserId == UserHandle.USER_SYSTEM; if (bootingSystemUser) { - mSystemServiceManager.startUser(t, currentUserId); + mSystemServiceManager.onUserStarting(t, currentUserId); } synchronized (this) { @@ -8816,37 +8816,6 @@ public class ActivityManagerService extends IActivityManager.Stub return -1; } - boolean dumpUids(PrintWriter pw, String dumpPackage, int dumpAppId, ActiveUids uids, - String header, boolean needSep) { - boolean printed = false; - for (int i=0; i<uids.size(); i++) { - UidRecord uidRec = uids.valueAt(i); - if (dumpPackage != null && UserHandle.getAppId(uidRec.uid) != dumpAppId) { - continue; - } - if (!printed) { - printed = true; - if (needSep) { - pw.println(); - } - pw.print(" "); - pw.println(header); - needSep = true; - } - pw.print(" UID "); UserHandle.formatUid(pw, uidRec.uid); - pw.print(": "); pw.println(uidRec); - pw.print(" curProcState="); pw.print(uidRec.mCurProcState); - pw.print(" curCapability="); - ActivityManager.printCapabilitiesFull(pw, uidRec.curCapability); - pw.println(); - for (int j = uidRec.procRecords.size() - 1; j >= 0; j--) { - pw.print(" proc="); - pw.println(uidRec.procRecords.valueAt(j)); - } - } - return printed; - } - void dumpBinderProxyInterfaceCounts(PrintWriter pw, String header) { final BinderProxy.InterfaceCount[] proxyCounts = BinderProxy.getSortedInterfaceCounts(50); @@ -9096,19 +9065,13 @@ public class ActivityManagerService extends IActivityManager.Stub needSep = dumpProcessesToGc(pw, needSep, dumpPackage); if (mProcessList.mActiveUids.size() > 0) { - if (dumpUids(pw, dumpPackage, dumpAppId, mProcessList.mActiveUids, - "UID states:", needSep)) { - needSep = true; - } + needSep |= mProcessList.mActiveUids.dump(pw, dumpPackage, dumpAppId, + "UID states:", needSep); } if (dumpAll) { - if (mUidObserverController.mValidateUids.size() > 0) { - if (dumpUids(pw, dumpPackage, dumpAppId, mUidObserverController.mValidateUids, - "UID validation:", needSep)) { - needSep = true; - } - } + needSep |= mUidObserverController.dumpValidateUids(pw, + dumpPackage, dumpAppId, "UID validation:", needSep); } if (needSep) { @@ -9377,22 +9340,12 @@ public class ActivityManagerService extends IActivityManager.Stub ActivityManagerServiceDumpProcessesProto.ACTIVE_INSTRUMENTATIONS); } - int whichAppId = getAppId(dumpPackage); - for (int i = 0; i < mProcessList.mActiveUids.size(); i++) { - UidRecord uidRec = mProcessList.mActiveUids.valueAt(i); - if (dumpPackage != null && UserHandle.getAppId(uidRec.uid) != whichAppId) { - continue; - } - uidRec.dumpDebug(proto, ActivityManagerServiceDumpProcessesProto.ACTIVE_UIDS); - } + final int dumpAppId = getAppId(dumpPackage); + mProcessList.mActiveUids.dumpProto(proto, dumpPackage, dumpAppId, + ActivityManagerServiceDumpProcessesProto.ACTIVE_UIDS); - for (int i = 0; i < mUidObserverController.mValidateUids.size(); i++) { - UidRecord uidRec = mUidObserverController.mValidateUids.valueAt(i); - if (dumpPackage != null && UserHandle.getAppId(uidRec.uid) != whichAppId) { - continue; - } - uidRec.dumpDebug(proto, ActivityManagerServiceDumpProcessesProto.VALIDATE_UIDS); - } + mUidObserverController.dumpValidateUidsProto(proto, dumpPackage, dumpAppId, + ActivityManagerServiceDumpProcessesProto.VALIDATE_UIDS); if (mProcessList.getLruSizeLocked() > 0) { long lruToken = proto.start(ActivityManagerServiceDumpProcessesProto.LRU_PROCS); @@ -14892,6 +14845,59 @@ public class ActivityManagerService extends IActivityManager.Stub return false; } + private boolean isEphemeralLocked(int uid) { + final String[] packages = mContext.getPackageManager().getPackagesForUid(uid); + if (packages == null || packages.length != 1) { // Ephemeral apps cannot share uid + return false; + } + return getPackageManagerInternalLocked().isPackageEphemeral( + UserHandle.getUserId(uid), packages[0]); + } + + void enqueueUidChangeLocked(UidRecord uidRec, int uid, int change) { + uid = uidRec != null ? uidRec.uid : uid; + if (uid < 0) { + throw new IllegalArgumentException("No UidRecord or uid"); + } + + final int procState = uidRec != null + ? uidRec.setProcState : PROCESS_STATE_NONEXISTENT; + final long procStateSeq = uidRec != null ? uidRec.curProcStateSeq : 0; + final int capability = uidRec != null ? uidRec.setCapability : 0; + final boolean ephemeral = uidRec != null ? uidRec.ephemeral : isEphemeralLocked(uid); + + if (uidRec != null && !uidRec.idle && (change & UidRecord.CHANGE_GONE) != 0) { + // If this uid is going away, and we haven't yet reported it is gone, + // then do so now. + change |= UidRecord.CHANGE_IDLE; + } + final int enqueuedChange = mUidObserverController.enqueueUidChange( + uidRec == null ? null : uidRec.pendingChange, + uid, change, procState, procStateSeq, capability, ephemeral); + if (uidRec != null) { + uidRec.lastReportedChange = enqueuedChange; + uidRec.updateLastDispatchedProcStateSeq(enqueuedChange); + } + + // Directly update the power manager, since we sit on top of it and it is critical + // it be kept in sync (so wake locks will be held as soon as appropriate). + if (mLocalPowerManager != null) { + // TODO: dispatch cached/uncached changes here, so we don't need to report + // all proc state changes. + if ((enqueuedChange & UidRecord.CHANGE_ACTIVE) != 0) { + mLocalPowerManager.uidActive(uid); + } + if ((enqueuedChange & UidRecord.CHANGE_IDLE) != 0) { + mLocalPowerManager.uidIdle(uid); + } + if ((enqueuedChange & UidRecord.CHANGE_GONE) != 0) { + mLocalPowerManager.uidGone(uid); + } else { + mLocalPowerManager.updateUidProcState(uid, procState); + } + } + } + final void setProcessTrackerStateLocked(ProcessRecord proc, int memFactor, long now) { synchronized (mProcessStats.mLock) { if (proc.thread != null && proc.baseProcessTracker != null) { @@ -15157,7 +15163,7 @@ public class ActivityManagerService extends IActivityManager.Stub @GuardedBy("this") final void doStopUidLocked(int uid, final UidRecord uidRec) { mServices.stopInBackgroundLocked(uid); - mUidObserverController.enqueueUidChangeLocked(uidRec, uid, UidRecord.CHANGE_IDLE); + enqueueUidChangeLocked(uidRec, uid, UidRecord.CHANGE_IDLE); } /** @@ -16707,7 +16713,7 @@ public class ActivityManagerService extends IActivityManager.Stub + totalTime + ". Uid: " + callingUid + " procStateSeq: " + procStateSeq + " UidRec: " + record + " validateUidRec: " - + mUidObserverController.mValidateUids.get(callingUid)); + + mUidObserverController.getValidateUidRecord(callingUid)); } } catch (InterruptedException e) { Thread.currentThread().interrupt(); diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java index 3eb4ddebaf06..6f706acf1706 100644 --- a/services/core/java/com/android/server/am/BatteryStatsService.java +++ b/services/core/java/com/android/server/am/BatteryStatsService.java @@ -651,12 +651,13 @@ public final class BatteryStatsService extends IBatteryStats.Stub public void noteWakupAlarm(final String name, final int uid, final WorkSource workSource, final String tag) { enforceCallingPermission(); + final WorkSource localWs = workSource != null ? new WorkSource(workSource) : null; synchronized (mLock) { final long elapsedRealtime = SystemClock.elapsedRealtime(); final long uptime = SystemClock.uptimeMillis(); mHandler.post(() -> { synchronized (mStats) { - mStats.noteWakupAlarmLocked(name, uid, workSource, tag, + mStats.noteWakupAlarmLocked(name, uid, localWs, tag, elapsedRealtime, uptime); } }); @@ -665,12 +666,13 @@ public final class BatteryStatsService extends IBatteryStats.Stub public void noteAlarmStart(final String name, final WorkSource workSource, final int uid) { enforceCallingPermission(); + final WorkSource localWs = workSource != null ? new WorkSource(workSource) : null; synchronized (mLock) { final long elapsedRealtime = SystemClock.elapsedRealtime(); final long uptime = SystemClock.uptimeMillis(); mHandler.post(() -> { synchronized (mStats) { - mStats.noteAlarmStartLocked(name, workSource, uid, elapsedRealtime, uptime); + mStats.noteAlarmStartLocked(name, localWs, uid, elapsedRealtime, uptime); } }); } @@ -678,12 +680,13 @@ public final class BatteryStatsService extends IBatteryStats.Stub public void noteAlarmFinish(final String name, final WorkSource workSource, final int uid) { enforceCallingPermission(); + final WorkSource localWs = workSource != null ? new WorkSource(workSource) : null; synchronized (mLock) { final long elapsedRealtime = SystemClock.elapsedRealtime(); final long uptime = SystemClock.uptimeMillis(); mHandler.post(() -> { synchronized (mStats) { - mStats.noteAlarmFinishLocked(name, workSource, uid, elapsedRealtime, uptime); + mStats.noteAlarmFinishLocked(name, localWs, uid, elapsedRealtime, uptime); } }); } @@ -722,12 +725,13 @@ public final class BatteryStatsService extends IBatteryStats.Stub public void noteStartWakelockFromSource(final WorkSource ws, final int pid, final String name, final String historyName, final int type, final boolean unimportantForLogging) { enforceCallingPermission(); + final WorkSource localWs = ws != null ? new WorkSource(ws) : null; synchronized (mLock) { final long elapsedRealtime = SystemClock.elapsedRealtime(); final long uptime = SystemClock.uptimeMillis(); mHandler.post(() -> { synchronized (mStats) { - mStats.noteStartWakeFromSourceLocked(ws, pid, name, historyName, + mStats.noteStartWakeFromSourceLocked(localWs, pid, name, historyName, type, unimportantForLogging, elapsedRealtime, uptime); } }); @@ -739,13 +743,15 @@ public final class BatteryStatsService extends IBatteryStats.Stub final String newName, final String newHistoryName, final int newType, final boolean newUnimportantForLogging) { enforceCallingPermission(); + final WorkSource localWs = ws != null ? new WorkSource(ws) : null; + final WorkSource localNewWs = newWs != null ? new WorkSource(newWs) : null; synchronized (mLock) { final long elapsedRealtime = SystemClock.elapsedRealtime(); final long uptime = SystemClock.uptimeMillis(); mHandler.post(() -> { synchronized (mStats) { - mStats.noteChangeWakelockFromSourceLocked(ws, pid, name, historyName, type, - newWs, newPid, newName, newHistoryName, newType, + mStats.noteChangeWakelockFromSourceLocked(localWs, pid, name, historyName, type, + localNewWs, newPid, newName, newHistoryName, newType, newUnimportantForLogging, elapsedRealtime, uptime); } }); @@ -755,12 +761,13 @@ public final class BatteryStatsService extends IBatteryStats.Stub public void noteStopWakelockFromSource(final WorkSource ws, final int pid, final String name, final String historyName, final int type) { enforceCallingPermission(); + final WorkSource localWs = ws != null ? new WorkSource(ws) : null; synchronized (mLock) { final long elapsedRealtime = SystemClock.elapsedRealtime(); final long uptime = SystemClock.uptimeMillis(); mHandler.post(() -> { synchronized (mStats) { - mStats.noteStopWakeFromSourceLocked(ws, pid, name, historyName, type, + mStats.noteStopWakeFromSourceLocked(localWs, pid, name, historyName, type, elapsedRealtime, uptime); } }); @@ -787,12 +794,13 @@ public final class BatteryStatsService extends IBatteryStats.Stub public void noteLongPartialWakelockStartFromSource(final String name, final String historyName, final WorkSource workSource) { enforceCallingPermission(); + final WorkSource localWs = workSource != null ? new WorkSource(workSource) : null; synchronized (mLock) { final long elapsedRealtime = SystemClock.elapsedRealtime(); final long uptime = SystemClock.uptimeMillis(); mHandler.post(() -> { synchronized (mStats) { - mStats.noteLongPartialWakelockStartFromSource(name, historyName, workSource, + mStats.noteLongPartialWakelockStartFromSource(name, historyName, localWs, elapsedRealtime, uptime); } }); @@ -819,12 +827,13 @@ public final class BatteryStatsService extends IBatteryStats.Stub public void noteLongPartialWakelockFinishFromSource(final String name, final String historyName, final WorkSource workSource) { enforceCallingPermission(); + final WorkSource localWs = workSource != null ? new WorkSource(workSource) : null; synchronized (mLock) { final long elapsedRealtime = SystemClock.elapsedRealtime(); final long uptime = SystemClock.uptimeMillis(); mHandler.post(() -> { synchronized (mStats) { - mStats.noteLongPartialWakelockFinishFromSource(name, historyName, workSource, + mStats.noteLongPartialWakelockFinishFromSource(name, historyName, localWs, elapsedRealtime, uptime); } }); @@ -890,12 +899,14 @@ public final class BatteryStatsService extends IBatteryStats.Stub @Override public void noteGpsChanged(final WorkSource oldWs, final WorkSource newWs) { enforceCallingPermission(); + final WorkSource localOldWs = oldWs != null ? new WorkSource(oldWs) : null; + final WorkSource localNewWs = newWs != null ? new WorkSource(newWs) : null; synchronized (mLock) { final long elapsedRealtime = SystemClock.elapsedRealtime(); final long uptime = SystemClock.uptimeMillis(); mHandler.post(() -> { synchronized (mStats) { - mStats.noteGpsChangedLocked(oldWs, newWs, elapsedRealtime, uptime); + mStats.noteGpsChangedLocked(localOldWs, localNewWs, elapsedRealtime, uptime); } }); } @@ -1322,12 +1333,13 @@ public final class BatteryStatsService extends IBatteryStats.Stub public void noteWifiRunning(final WorkSource ws) { enforceCallingPermission(); + final WorkSource localWs = ws != null ? new WorkSource(ws) : null; synchronized (mLock) { final long elapsedRealtime = SystemClock.elapsedRealtime(); final long uptime = SystemClock.uptimeMillis(); mHandler.post(() -> { synchronized (mStats) { - mStats.noteWifiRunningLocked(ws, elapsedRealtime, uptime); + mStats.noteWifiRunningLocked(localWs, elapsedRealtime, uptime); } // TODO: Log WIFI_RUNNING_STATE_CHANGED in a better spot to include Hotspot too. FrameworkStatsLog.write(FrameworkStatsLog.WIFI_RUNNING_STATE_CHANGED, @@ -1338,12 +1350,15 @@ public final class BatteryStatsService extends IBatteryStats.Stub public void noteWifiRunningChanged(final WorkSource oldWs, final WorkSource newWs) { enforceCallingPermission(); + final WorkSource localOldWs = oldWs != null ? new WorkSource(oldWs) : null; + final WorkSource localNewWs = newWs != null ? new WorkSource(newWs) : null; synchronized (mLock) { final long elapsedRealtime = SystemClock.elapsedRealtime(); final long uptime = SystemClock.uptimeMillis(); mHandler.post(() -> { synchronized (mStats) { - mStats.noteWifiRunningChangedLocked(oldWs, newWs, elapsedRealtime, uptime); + mStats.noteWifiRunningChangedLocked( + localOldWs, localNewWs, elapsedRealtime, uptime); } FrameworkStatsLog.write(FrameworkStatsLog.WIFI_RUNNING_STATE_CHANGED, newWs, FrameworkStatsLog.WIFI_RUNNING_STATE_CHANGED__STATE__ON); @@ -1355,12 +1370,13 @@ public final class BatteryStatsService extends IBatteryStats.Stub public void noteWifiStopped(final WorkSource ws) { enforceCallingPermission(); + final WorkSource localWs = ws != null ? new WorkSource(ws) : ws; synchronized (mLock) { final long elapsedRealtime = SystemClock.elapsedRealtime(); final long uptime = SystemClock.uptimeMillis(); mHandler.post(() -> { synchronized (mStats) { - mStats.noteWifiStoppedLocked(ws, elapsedRealtime, uptime); + mStats.noteWifiStoppedLocked(localWs, elapsedRealtime, uptime); } FrameworkStatsLog.write(FrameworkStatsLog.WIFI_RUNNING_STATE_CHANGED, ws, FrameworkStatsLog.WIFI_RUNNING_STATE_CHANGED__STATE__OFF); @@ -1487,12 +1503,14 @@ public final class BatteryStatsService extends IBatteryStats.Stub public void noteFullWifiLockAcquiredFromSource(final WorkSource ws) { enforceCallingPermission(); + final WorkSource localWs = ws != null ? new WorkSource(ws) : null; synchronized (mLock) { final long elapsedRealtime = SystemClock.elapsedRealtime(); final long uptime = SystemClock.uptimeMillis(); mHandler.post(() -> { synchronized (mStats) { - mStats.noteFullWifiLockAcquiredFromSourceLocked(ws, elapsedRealtime, uptime); + mStats.noteFullWifiLockAcquiredFromSourceLocked( + localWs, elapsedRealtime, uptime); } }); } @@ -1500,12 +1518,14 @@ public final class BatteryStatsService extends IBatteryStats.Stub public void noteFullWifiLockReleasedFromSource(final WorkSource ws) { enforceCallingPermission(); + final WorkSource localWs = ws != null ? new WorkSource(ws) : null; synchronized (mLock) { final long elapsedRealtime = SystemClock.elapsedRealtime(); final long uptime = SystemClock.uptimeMillis(); mHandler.post(() -> { synchronized (mStats) { - mStats.noteFullWifiLockReleasedFromSourceLocked(ws, elapsedRealtime, uptime); + mStats.noteFullWifiLockReleasedFromSourceLocked( + localWs, elapsedRealtime, uptime); } }); } @@ -1513,12 +1533,13 @@ public final class BatteryStatsService extends IBatteryStats.Stub public void noteWifiScanStartedFromSource(final WorkSource ws) { enforceCallingPermission(); + final WorkSource localWs = ws != null ? new WorkSource(ws) : null; synchronized (mLock) { final long elapsedRealtime = SystemClock.elapsedRealtime(); final long uptime = SystemClock.uptimeMillis(); mHandler.post(() -> { synchronized (mStats) { - mStats.noteWifiScanStartedFromSourceLocked(ws, elapsedRealtime, uptime); + mStats.noteWifiScanStartedFromSourceLocked(localWs, elapsedRealtime, uptime); } }); } @@ -1526,12 +1547,13 @@ public final class BatteryStatsService extends IBatteryStats.Stub public void noteWifiScanStoppedFromSource(final WorkSource ws) { enforceCallingPermission(); + final WorkSource localWs = ws != null ? new WorkSource(ws) : null; synchronized (mLock) { final long elapsedRealtime = SystemClock.elapsedRealtime(); final long uptime = SystemClock.uptimeMillis(); mHandler.post(() -> { synchronized (mStats) { - mStats.noteWifiScanStoppedFromSourceLocked(ws, elapsedRealtime, uptime); + mStats.noteWifiScanStoppedFromSourceLocked(localWs, elapsedRealtime, uptime); } }); } @@ -1539,12 +1561,13 @@ public final class BatteryStatsService extends IBatteryStats.Stub public void noteWifiBatchedScanStartedFromSource(final WorkSource ws, final int csph) { enforceCallingPermission(); + final WorkSource localWs = ws != null ? new WorkSource(ws) : null; synchronized (mLock) { final long elapsedRealtime = SystemClock.elapsedRealtime(); final long uptime = SystemClock.uptimeMillis(); mHandler.post(() -> { synchronized (mStats) { - mStats.noteWifiBatchedScanStartedFromSourceLocked(ws, csph, + mStats.noteWifiBatchedScanStartedFromSourceLocked(localWs, csph, elapsedRealtime, uptime); } }); @@ -1553,12 +1576,14 @@ public final class BatteryStatsService extends IBatteryStats.Stub public void noteWifiBatchedScanStoppedFromSource(final WorkSource ws) { enforceCallingPermission(); + final WorkSource localWs = ws != null ? new WorkSource(ws) : null; synchronized (mLock) { final long elapsedRealtime = SystemClock.elapsedRealtime(); final long uptime = SystemClock.uptimeMillis(); mHandler.post(() -> { synchronized (mStats) { - mStats.noteWifiBatchedScanStoppedFromSourceLocked(ws, elapsedRealtime, uptime); + mStats.noteWifiBatchedScanStoppedFromSourceLocked( + localWs, elapsedRealtime, uptime); } }); } @@ -1635,12 +1660,13 @@ public final class BatteryStatsService extends IBatteryStats.Stub @Override public void noteBleScanStarted(final WorkSource ws, final boolean isUnoptimized) { enforceCallingPermission(); + final WorkSource localWs = ws != null ? new WorkSource(ws) : null; synchronized (mLock) { final long elapsedRealtime = SystemClock.elapsedRealtime(); final long uptime = SystemClock.uptimeMillis(); mHandler.post(() -> { synchronized (mStats) { - mStats.noteBluetoothScanStartedFromSourceLocked(ws, isUnoptimized, + mStats.noteBluetoothScanStartedFromSourceLocked(localWs, isUnoptimized, elapsedRealtime, uptime); } }); @@ -1650,12 +1676,13 @@ public final class BatteryStatsService extends IBatteryStats.Stub @Override public void noteBleScanStopped(final WorkSource ws, final boolean isUnoptimized) { enforceCallingPermission(); + final WorkSource localWs = ws != null ? new WorkSource(ws) : null; synchronized (mLock) { final long elapsedRealtime = SystemClock.elapsedRealtime(); final long uptime = SystemClock.uptimeMillis(); mHandler.post(() -> { synchronized (mStats) { - mStats.noteBluetoothScanStoppedFromSourceLocked(ws, isUnoptimized, + mStats.noteBluetoothScanStoppedFromSourceLocked(localWs, isUnoptimized, uptime, elapsedRealtime); } }); @@ -1679,12 +1706,13 @@ public final class BatteryStatsService extends IBatteryStats.Stub @Override public void noteBleScanResults(final WorkSource ws, final int numNewResults) { enforceCallingPermission(); + final WorkSource localWs = ws != null ? new WorkSource(ws) : null; synchronized (mLock) { final long elapsedRealtime = SystemClock.elapsedRealtime(); final long uptime = SystemClock.uptimeMillis(); mHandler.post(() -> { synchronized (mStats) { - mStats.noteBluetoothScanResultsFromSourceLocked(ws, numNewResults, + mStats.noteBluetoothScanResultsFromSourceLocked(localWs, numNewResults, elapsedRealtime, uptime); } }); diff --git a/services/core/java/com/android/server/am/CoreSettingsObserver.java b/services/core/java/com/android/server/am/CoreSettingsObserver.java index 529c6516398e..f32423f75324 100644 --- a/services/core/java/com/android/server/am/CoreSettingsObserver.java +++ b/services/core/java/com/android/server/am/CoreSettingsObserver.java @@ -85,19 +85,19 @@ final class CoreSettingsObserver extends ContentObserver { sGlobalSettingToTypeMap.put( Settings.Global.DEBUG_VIEW_ATTRIBUTES_APPLICATION_PACKAGE, String.class); sGlobalSettingToTypeMap.put( - Settings.Global.GLOBAL_SETTINGS_ANGLE_DEBUG_PACKAGE, String.class); + Settings.Global.ANGLE_DEBUG_PACKAGE, String.class); sGlobalSettingToTypeMap.put( - Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_ALL_ANGLE, int.class); + Settings.Global.ANGLE_GL_DRIVER_ALL_ANGLE, int.class); sGlobalSettingToTypeMap.put( - Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_SELECTION_PKGS, String.class); + Settings.Global.ANGLE_GL_DRIVER_SELECTION_PKGS, String.class); sGlobalSettingToTypeMap.put( - Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_SELECTION_VALUES, String.class); + Settings.Global.ANGLE_GL_DRIVER_SELECTION_VALUES, String.class); sGlobalSettingToTypeMap.put( - Settings.Global.GLOBAL_SETTINGS_ANGLE_ALLOWLIST, String.class); + Settings.Global.ANGLE_ALLOWLIST, String.class); sGlobalSettingToTypeMap.put( Settings.Global.ANGLE_EGL_FEATURES, String.class); sGlobalSettingToTypeMap.put( - Settings.Global.GLOBAL_SETTINGS_SHOW_ANGLE_IN_USE_DIALOG_BOX, String.class); + Settings.Global.SHOW_ANGLE_IN_USE_DIALOG_BOX, String.class); sGlobalSettingToTypeMap.put(Settings.Global.ENABLE_GPU_DEBUG_LAYERS, int.class); sGlobalSettingToTypeMap.put(Settings.Global.GPU_DEBUG_APP, String.class); sGlobalSettingToTypeMap.put(Settings.Global.GPU_DEBUG_LAYERS, String.class); diff --git a/services/core/java/com/android/server/am/EventLogTags.logtags b/services/core/java/com/android/server/am/EventLogTags.logtags index b818ccf275c4..68f2e35094fc 100644 --- a/services/core/java/com/android/server/am/EventLogTags.logtags +++ b/services/core/java/com/android/server/am/EventLogTags.logtags @@ -104,6 +104,14 @@ option java_package com.android.server.am 30076 uc_start_user_internal (userId|1|5) 30077 uc_unlock_user (userId|1|5) 30078 uc_finish_user_boot (userId|1|5) -30079 uc_dispatch_user_switch (oldUserId|1|5) (newUserId|1|5) -30080 uc_continue_user_switch (oldUserId|1|5) (newUserId|1|5) -30081 uc_send_user_broadcast (userId|1|5),(IntentAction|3)
\ No newline at end of file +30079 uc_dispatch_user_switch (oldUserId|1|5),(newUserId|1|5) +30080 uc_continue_user_switch (oldUserId|1|5),(newUserId|1|5) +30081 uc_send_user_broadcast (userId|1|5),(IntentAction|3) +# Tags below are used by SystemServiceManager - although it's technically part of am, these are +# also user switch events and useful to be analyzed together with events above. +30082 ssm_user_starting (userId|1|5) +30083 ssm_user_switching (oldUserId|1|5),(newUserId|1|5) +30084 ssm_user_unlocking (userId|1|5) +30085 ssm_user_unlocked (userId|1|5) +30086 ssm_user_stopping (userId|1|5) +30087 ssm_user_stopped (userId|1|5) diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java index 58ac2dc869ce..9d49236191a9 100644 --- a/services/core/java/com/android/server/am/OomAdjuster.java +++ b/services/core/java/com/android/server/am/OomAdjuster.java @@ -1147,7 +1147,7 @@ public final class OomAdjuster { uidRec.setWhitelist = uidRec.curWhitelist; uidRec.setIdle = uidRec.idle; mService.mAtmInternal.onUidProcStateChanged(uidRec.uid, uidRec.setProcState); - mService.mUidObserverController.enqueueUidChangeLocked(uidRec, -1, uidChange); + mService.enqueueUidChangeLocked(uidRec, -1, uidChange); mService.noteUidProcessState(uidRec.uid, uidRec.getCurProcState(), uidRec.curCapability); if (uidRec.foregroundServices) { diff --git a/services/core/java/com/android/server/am/PreBootBroadcaster.java b/services/core/java/com/android/server/am/PreBootBroadcaster.java index 747e8a89f8e7..be63dd41735b 100644 --- a/services/core/java/com/android/server/am/PreBootBroadcaster.java +++ b/services/core/java/com/android/server/am/PreBootBroadcaster.java @@ -144,7 +144,8 @@ public abstract class PreBootBroadcaster extends IIntentReceiver.Stub { final PendingIntent contentIntent; if (context.getPackageManager().resolveActivity(intent, 0) != null) { - contentIntent = PendingIntent.getActivity(context, 0, intent, 0); + contentIntent = PendingIntent.getActivity(context, 0, intent, + PendingIntent.FLAG_IMMUTABLE); } else { contentIntent = null; } diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java index cf0223bac289..6f6cad043a42 100644 --- a/services/core/java/com/android/server/am/ProcessList.java +++ b/services/core/java/com/android/server/am/ProcessList.java @@ -2948,7 +2948,7 @@ public final class ProcessList { // No more processes using this uid, tell clients it is gone. if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS, "No more processes in " + uidRecord); - mService.mUidObserverController.enqueueUidChangeLocked(uidRecord, -1, + mService.enqueueUidChangeLocked(uidRecord, -1, UidRecord.CHANGE_GONE); EventLogTags.writeAmUidStopped(uid); mActiveUids.remove(uid); diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java index 2571205bb3af..53c6758585cf 100644 --- a/services/core/java/com/android/server/am/ProcessRecord.java +++ b/services/core/java/com/android/server/am/ProcessRecord.java @@ -1460,6 +1460,12 @@ class ProcessRecord implements WindowProcessListener { if (updateServiceConnectionActivities) { mService.mServices.updateServiceConnectionActivitiesLocked(this); } + if (thread == null) { + // Only update lru and oom-adj if the process is alive. Because it may be called + // when cleaning up the last activity from handling process died, the dead process + // should not be added to lru list again. + return; + } mService.mProcessList.updateLruProcessLocked(this, activityChange, null /* client */); if (updateOomAdj) { mService.updateOomAdjLocked(this, OomAdjuster.OOM_ADJ_REASON_ACTIVITY); diff --git a/services/core/java/com/android/server/am/UidObserverController.java b/services/core/java/com/android/server/am/UidObserverController.java index 4d9260aec62e..b3150d1489bb 100644 --- a/services/core/java/com/android/server/am/UidObserverController.java +++ b/services/core/java/com/android/server/am/UidObserverController.java @@ -20,9 +20,12 @@ import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_UID_OBSERVERS; import static com.android.server.am.ActivityManagerService.TAG_UID_OBSERVERS; +import android.annotation.NonNull; +import android.annotation.Nullable; import android.app.ActivityManager; import android.app.ActivityManagerProto; import android.app.IUidObserver; +import android.os.Handler; import android.os.RemoteCallbackList; import android.os.RemoteException; import android.os.SystemClock; @@ -40,158 +43,154 @@ import java.io.PrintWriter; import java.util.ArrayList; public class UidObserverController { - private final ActivityManagerService mService; + /** If a UID observer takes more than this long, send a WTF. */ + private static final int SLOW_UID_OBSERVER_THRESHOLD_MS = 20; + + private final Handler mHandler; + + private final Object mLock = new Object(); + + @GuardedBy("mLock") final RemoteCallbackList<IUidObserver> mUidObservers = new RemoteCallbackList<>(); - UidRecord.ChangeItem[] mActiveUidChanges = new UidRecord.ChangeItem[5]; - final ArrayList<UidRecord.ChangeItem> mPendingUidChanges = new ArrayList<>(); - final ArrayList<UidRecord.ChangeItem> mAvailUidChanges = new ArrayList<>(); + @GuardedBy("mLock") + private final ArrayList<ChangeRecord> mPendingUidChanges = new ArrayList<>(); + @GuardedBy("mLock") + private final ArrayList<ChangeRecord> mAvailUidChanges = new ArrayList<>(); + + private ChangeRecord[] mActiveUidChanges = new ChangeRecord[5]; /** Total # of UID change events dispatched, shown in dumpsys. */ - int mUidChangeDispatchCount; + @GuardedBy("mLock") + private int mUidChangeDispatchCount; - /** If a UID observer takes more than this long, send a WTF. */ - private static final int SLOW_UID_OBSERVER_THRESHOLD_MS = 20; + private final Runnable mDispatchRunnable = this::dispatchUidsChanged; /** * This is for verifying the UID report flow. */ - static final boolean VALIDATE_UID_STATES = true; - final ActiveUids mValidateUids; + private static final boolean VALIDATE_UID_STATES = true; + private final ActiveUids mValidateUids; - UidObserverController(ActivityManagerService service) { - mService = service; - mValidateUids = new ActiveUids(mService, false /* postChangesToAtm */); + UidObserverController(@NonNull Handler handler) { + mHandler = handler; + mValidateUids = new ActiveUids(null /* service */, false /* postChangesToAtm */); } - @GuardedBy("mService") - void register(IUidObserver observer, int which, int cutpoint, String callingPackage, - int callingUid) { - mUidObservers.register(observer, new UidObserverRegistration(callingUid, - callingPackage, which, cutpoint)); + void register(@NonNull IUidObserver observer, int which, int cutpoint, + @NonNull String callingPackage, int callingUid) { + synchronized (mLock) { + mUidObservers.register(observer, new UidObserverRegistration(callingUid, + callingPackage, which, cutpoint)); + } } - @GuardedBy("mService") - void unregister(IUidObserver observer) { - mUidObservers.unregister(observer); + void unregister(@NonNull IUidObserver observer) { + synchronized (mLock) { + mUidObservers.unregister(observer); + } } - @GuardedBy("mService") - final void enqueueUidChangeLocked(UidRecord uidRec, int uid, int change) { - final UidRecord.ChangeItem pendingChange; - if (uidRec == null || uidRec.pendingChange == null) { + int enqueueUidChange(@Nullable ChangeRecord currentRecord, int uid, int change, int procState, + long procStateSeq, int capability, boolean ephemeral) { + synchronized (mLock) { if (mPendingUidChanges.size() == 0) { if (DEBUG_UID_OBSERVERS) { Slog.i(TAG_UID_OBSERVERS, "*** Enqueueing dispatch uid changed!"); } - mService.mUiHandler.post(this::dispatchUidsChanged); + mHandler.post(mDispatchRunnable); } - final int size = mAvailUidChanges.size(); - if (size > 0) { - pendingChange = mAvailUidChanges.remove(size - 1); - if (DEBUG_UID_OBSERVERS) { - Slog.i(TAG_UID_OBSERVERS, "Retrieving available item: " + pendingChange); - } + + final ChangeRecord changeRecord = currentRecord != null + ? currentRecord : getOrCreateChangeRecordLocked(); + if (!changeRecord.isPending) { + changeRecord.isPending = true; + mPendingUidChanges.add(changeRecord); } else { - pendingChange = new UidRecord.ChangeItem(); - if (DEBUG_UID_OBSERVERS) { - Slog.i(TAG_UID_OBSERVERS, "Allocating new item: " + pendingChange); - } - } - if (uidRec != null) { - uidRec.pendingChange = pendingChange; - if ((change & UidRecord.CHANGE_GONE) != 0 && !uidRec.idle) { - // If this uid is going away, and we haven't yet reported it is gone, - // then do so now. - change |= UidRecord.CHANGE_IDLE; - } - } else if (uid < 0) { - throw new IllegalArgumentException("No UidRecord or uid"); - } - pendingChange.uidRecord = uidRec; - pendingChange.uid = uidRec != null ? uidRec.uid : uid; - mPendingUidChanges.add(pendingChange); - } else { - pendingChange = uidRec.pendingChange; - // If there is no change in idle or active state, then keep whatever was pending. - if ((change & (UidRecord.CHANGE_IDLE | UidRecord.CHANGE_ACTIVE)) == 0) { - change |= (pendingChange.change & (UidRecord.CHANGE_IDLE - | UidRecord.CHANGE_ACTIVE)); - } - // If there is no change in cached or uncached state, then keep whatever was pending. - if ((change & (UidRecord.CHANGE_CACHED | UidRecord.CHANGE_UNCACHED)) == 0) { - change |= (pendingChange.change & (UidRecord.CHANGE_CACHED - | UidRecord.CHANGE_UNCACHED)); - } - // If this is a report of the UID being gone, then we shouldn't keep any previous - // report of it being active or cached. (That is, a gone uid is never active, - // and never cached.) - if ((change & UidRecord.CHANGE_GONE) != 0) { - change &= ~(UidRecord.CHANGE_ACTIVE | UidRecord.CHANGE_CACHED); - if (!uidRec.idle) { - // If this uid is going away, and we haven't yet reported it is gone, - // then do so now. - change |= UidRecord.CHANGE_IDLE; - } + change = mergeWithPendingChange(change, changeRecord.change); } + + changeRecord.uid = uid; + changeRecord.change = change; + changeRecord.procState = procState; + changeRecord.procStateSeq = procStateSeq; + changeRecord.capability = capability; + changeRecord.ephemeral = ephemeral; + + return changeRecord.change; } - pendingChange.change = change; - pendingChange.processState = uidRec != null - ? uidRec.setProcState : PROCESS_STATE_NONEXISTENT; - pendingChange.capability = uidRec != null ? uidRec.setCapability : 0; - pendingChange.ephemeral = uidRec != null ? uidRec.ephemeral : isEphemeralLocked(uid); - pendingChange.procStateSeq = uidRec != null ? uidRec.curProcStateSeq : 0; - if (uidRec != null) { - uidRec.lastReportedChange = change; - uidRec.updateLastDispatchedProcStateSeq(change); + } + + ArrayList<ChangeRecord> getPendingUidChangesForTest() { + return mPendingUidChanges; + } + + ActiveUids getValidateUidsForTest() { + return mValidateUids; + } + + @VisibleForTesting + static int mergeWithPendingChange(int currentChange, int pendingChange) { + // If there is no change in idle or active state, then keep whatever was pending. + if ((currentChange & (UidRecord.CHANGE_IDLE | UidRecord.CHANGE_ACTIVE)) == 0) { + currentChange |= (pendingChange & (UidRecord.CHANGE_IDLE + | UidRecord.CHANGE_ACTIVE)); } + // If there is no change in cached or uncached state, then keep whatever was pending. + if ((currentChange & (UidRecord.CHANGE_CACHED | UidRecord.CHANGE_UNCACHED)) == 0) { + currentChange |= (pendingChange & (UidRecord.CHANGE_CACHED + | UidRecord.CHANGE_UNCACHED)); + } + // If this is a report of the UID being gone, then we shouldn't keep any previous + // report of it being active or cached. (That is, a gone uid is never active, + // and never cached.) + if ((currentChange & UidRecord.CHANGE_GONE) != 0) { + currentChange &= ~(UidRecord.CHANGE_ACTIVE | UidRecord.CHANGE_CACHED); + } + return currentChange; + } - // Directly update the power manager, since we sit on top of it and it is critical - // it be kept in sync (so wake locks will be held as soon as appropriate). - if (mService.mLocalPowerManager != null) { - // TO DO: dispatch cached/uncached changes here, so we don't need to report - // all proc state changes. - if ((change & UidRecord.CHANGE_ACTIVE) != 0) { - mService.mLocalPowerManager.uidActive(pendingChange.uid); - } - if ((change & UidRecord.CHANGE_IDLE) != 0) { - mService.mLocalPowerManager.uidIdle(pendingChange.uid); + @GuardedBy("mLock") + private ChangeRecord getOrCreateChangeRecordLocked() { + final ChangeRecord changeRecord; + final int size = mAvailUidChanges.size(); + if (size > 0) { + changeRecord = mAvailUidChanges.remove(size - 1); + if (DEBUG_UID_OBSERVERS) { + Slog.i(TAG_UID_OBSERVERS, "Retrieving available item: " + changeRecord); } - if ((change & UidRecord.CHANGE_GONE) != 0) { - mService.mLocalPowerManager.uidGone(pendingChange.uid); - } else { - mService.mLocalPowerManager.updateUidProcState(pendingChange.uid, - pendingChange.processState); + } else { + changeRecord = new ChangeRecord(); + if (DEBUG_UID_OBSERVERS) { + Slog.i(TAG_UID_OBSERVERS, "Allocating new item: " + changeRecord); } } + return changeRecord; } @VisibleForTesting void dispatchUidsChanged() { - int numUidChanges; - synchronized (mService) { + final int numUidChanges; + synchronized (mLock) { numUidChanges = mPendingUidChanges.size(); if (mActiveUidChanges.length < numUidChanges) { - mActiveUidChanges = new UidRecord.ChangeItem[numUidChanges]; + mActiveUidChanges = new ChangeRecord[numUidChanges]; } for (int i = 0; i < numUidChanges; i++) { - final UidRecord.ChangeItem change = mPendingUidChanges.get(i); - mActiveUidChanges[i] = change; - if (change.uidRecord != null) { - change.uidRecord.pendingChange = null; - change.uidRecord = null; - } + final ChangeRecord changeRecord = mPendingUidChanges.get(i); + mActiveUidChanges[i] = getOrCreateChangeRecordLocked(); + changeRecord.copyTo(mActiveUidChanges[i]); + changeRecord.isPending = false; } mPendingUidChanges.clear(); if (DEBUG_UID_OBSERVERS) { Slog.i(TAG_UID_OBSERVERS, "*** Delivering " + numUidChanges + " uid changes"); } + mUidChangeDispatchCount += numUidChanges; } - mUidChangeDispatchCount += numUidChanges; int i = mUidObservers.beginBroadcast(); - while (i > 0) { - i--; + while (i-- > 0) { dispatchUidsChangedForObserver(mUidObservers.getBroadcastItem(i), (UidObserverRegistration) mUidObservers.getBroadcastCookie(i), numUidChanges); } @@ -199,7 +198,7 @@ public class UidObserverController { if (VALIDATE_UID_STATES && mUidObservers.getRegisteredCallbackCount() > 0) { for (int j = 0; j < numUidChanges; ++j) { - final UidRecord.ChangeItem item = mActiveUidChanges[j]; + final ChangeRecord item = mActiveUidChanges[j]; if ((item.change & UidRecord.CHANGE_GONE) != 0) { mValidateUids.remove(item.uid); } else { @@ -213,28 +212,30 @@ public class UidObserverController { } else if ((item.change & UidRecord.CHANGE_ACTIVE) != 0) { validateUid.idle = false; } - validateUid.setCurProcState(validateUid.setProcState = item.processState); + validateUid.setCurProcState(validateUid.setProcState = item.procState); validateUid.curCapability = validateUid.setCapability = item.capability; validateUid.lastDispatchedProcStateSeq = item.procStateSeq; } } } - synchronized (mService) { + synchronized (mLock) { for (int j = 0; j < numUidChanges; j++) { - mAvailUidChanges.add(mActiveUidChanges[j]); + final ChangeRecord changeRecord = mActiveUidChanges[j]; + changeRecord.isPending = false; + mAvailUidChanges.add(changeRecord); } } } - private void dispatchUidsChangedForObserver(IUidObserver observer, - UidObserverRegistration reg, int changesSize) { + private void dispatchUidsChangedForObserver(@NonNull IUidObserver observer, + @NonNull UidObserverRegistration reg, int changesSize) { if (observer == null) { return; } try { for (int j = 0; j < changesSize; j++) { - UidRecord.ChangeItem item = mActiveUidChanges[j]; + final ChangeRecord item = mActiveUidChanges[j]; final int change = item.change; if (change == UidRecord.CHANGE_PROCSTATE && (reg.mWhich & ActivityManager.UID_OBSERVER_PROCSTATE) == 0) { @@ -285,7 +286,7 @@ public class UidObserverController { if ((reg.mWhich & ActivityManager.UID_OBSERVER_PROCSTATE) != 0) { if (DEBUG_UID_OBSERVERS) { Slog.i(TAG_UID_OBSERVERS, "UID CHANGED uid=" + item.uid - + ": " + item.processState + ": " + item.capability); + + ": " + item.procState + ": " + item.capability); } boolean doReport = true; if (reg.mCutpoint >= ActivityManager.MIN_PROCESS_STATE) { @@ -293,17 +294,17 @@ public class UidObserverController { ActivityManager.PROCESS_STATE_UNKNOWN); if (lastState != ActivityManager.PROCESS_STATE_UNKNOWN) { final boolean lastAboveCut = lastState <= reg.mCutpoint; - final boolean newAboveCut = item.processState <= reg.mCutpoint; + final boolean newAboveCut = item.procState <= reg.mCutpoint; doReport = lastAboveCut != newAboveCut; } else { - doReport = item.processState != PROCESS_STATE_NONEXISTENT; + doReport = item.procState != PROCESS_STATE_NONEXISTENT; } } if (doReport) { if (reg.mLastProcStates != null) { - reg.mLastProcStates.put(item.uid, item.processState); + reg.mLastProcStates.put(item.uid, item.procState); } - observer.onUidStateChanged(item.uid, item.processState, + observer.onUidStateChanged(item.uid, item.procState, item.procStateSeq, item.capability); } } @@ -320,94 +321,93 @@ public class UidObserverController { } } - private boolean isEphemeralLocked(int uid) { - final String[] packages = mService.mContext.getPackageManager().getPackagesForUid(uid); - if (packages == null || packages.length != 1) { // Ephemeral apps cannot share uid - return false; - } - return mService.getPackageManagerInternalLocked().isPackageEphemeral( - UserHandle.getUserId(uid), packages[0]); + UidRecord getValidateUidRecord(int uid) { + return mValidateUids.get(uid); } - @GuardedBy("mService") - void dump(PrintWriter pw, String dumpPackage) { - final int count = mUidObservers.getRegisteredCallbackCount(); - boolean printed = false; - for (int i = 0; i < count; i++) { - final UidObserverRegistration reg = (UidObserverRegistration) - mUidObservers.getRegisteredCallbackCookie(i); - if (dumpPackage == null || dumpPackage.equals(reg.mPkg)) { - if (!printed) { - pw.println(" mUidObservers:"); - printed = true; - } - pw.print(" "); UserHandle.formatUid(pw, reg.mUid); - pw.print(" "); pw.print(reg.mPkg); - final IUidObserver observer = mUidObservers.getRegisteredCallbackItem(i); - pw.print(" "); pw.print(observer.getClass().getTypeName()); pw.print(":"); - if ((reg.mWhich & ActivityManager.UID_OBSERVER_IDLE) != 0) { - pw.print(" IDLE"); - } - if ((reg.mWhich & ActivityManager.UID_OBSERVER_ACTIVE) != 0) { - pw.print(" ACT"); - } - if ((reg.mWhich & ActivityManager.UID_OBSERVER_GONE) != 0) { - pw.print(" GONE"); - } - if ((reg.mWhich & ActivityManager.UID_OBSERVER_PROCSTATE) != 0) { - pw.print(" STATE"); - pw.print(" (cut="); pw.print(reg.mCutpoint); - pw.print(")"); - } - pw.println(); - if (reg.mLastProcStates != null) { - final int size = reg.mLastProcStates.size(); - for (int j = 0; j < size; j++) { - pw.print(" Last "); - UserHandle.formatUid(pw, reg.mLastProcStates.keyAt(j)); - pw.print(": "); pw.println(reg.mLastProcStates.valueAt(j)); + void dump(@NonNull PrintWriter pw, @Nullable String dumpPackage) { + synchronized (mLock) { + final int count = mUidObservers.getRegisteredCallbackCount(); + boolean printed = false; + for (int i = 0; i < count; i++) { + final UidObserverRegistration reg = (UidObserverRegistration) + mUidObservers.getRegisteredCallbackCookie(i); + if (dumpPackage == null || dumpPackage.equals(reg.mPkg)) { + if (!printed) { + pw.println(" mUidObservers:"); + printed = true; } + reg.dump(pw, mUidObservers.getRegisteredCallbackItem(i)); } } - } - pw.println(); - pw.print(" mUidChangeDispatchCount="); - pw.print(mUidChangeDispatchCount); - pw.println(); - pw.println(" Slow UID dispatches:"); - final int size = mUidObservers.beginBroadcast(); - for (int i = 0; i < size; i++) { - UidObserverRegistration r = - (UidObserverRegistration) mUidObservers.getBroadcastCookie(i); - pw.print(" "); - pw.print(mUidObservers.getBroadcastItem(i).getClass().getTypeName()); - pw.print(": "); - pw.print(r.mSlowDispatchCount); - pw.print(" / Max "); - pw.print(r.mMaxDispatchTime); - pw.println("ms"); + pw.println(); + pw.print(" mUidChangeDispatchCount="); + pw.print(mUidChangeDispatchCount); + pw.println(); + pw.println(" Slow UID dispatches:"); + for (int i = 0; i < count; i++) { + final UidObserverRegistration reg = (UidObserverRegistration) + mUidObservers.getRegisteredCallbackCookie(i); + pw.print(" "); + pw.print(mUidObservers.getRegisteredCallbackItem(i).getClass().getTypeName()); + pw.print(": "); + pw.print(reg.mSlowDispatchCount); + pw.print(" / Max "); + pw.print(reg.mMaxDispatchTime); + pw.println("ms"); + } } - mUidObservers.finishBroadcast(); } - @GuardedBy("mService") - void dumpDebug(ProtoOutputStream proto, String dumpPackage) { - final int count = mUidObservers.getRegisteredCallbackCount(); - for (int i = 0; i < count; i++) { - final UidObserverRegistration reg = (UidObserverRegistration) - mUidObservers.getRegisteredCallbackCookie(i); - if (dumpPackage == null || dumpPackage.equals(reg.mPkg)) { - reg.dumpDebug(proto, ActivityManagerServiceDumpProcessesProto.UID_OBSERVERS); + void dumpDebug(@NonNull ProtoOutputStream proto, @Nullable String dumpPackage) { + synchronized (mLock) { + final int count = mUidObservers.getRegisteredCallbackCount(); + for (int i = 0; i < count; i++) { + final UidObserverRegistration reg = (UidObserverRegistration) + mUidObservers.getRegisteredCallbackCookie(i); + if (dumpPackage == null || dumpPackage.equals(reg.mPkg)) { + reg.dumpDebug(proto, ActivityManagerServiceDumpProcessesProto.UID_OBSERVERS); + } } } } + boolean dumpValidateUids(@NonNull PrintWriter pw, @Nullable String dumpPackage, int dumpAppId, + @NonNull String header, boolean needSep) { + return mValidateUids.dump(pw, dumpPackage, dumpAppId, header, needSep); + } + + void dumpValidateUidsProto(@NonNull ProtoOutputStream proto, @Nullable String dumpPackage, + int dumpAppId, long fieldId) { + mValidateUids.dumpProto(proto, dumpPackage, dumpAppId, fieldId); + } + + static final class ChangeRecord { + public boolean isPending; + public int uid; + public int change; + public int procState; + public int capability; + public boolean ephemeral; + public long procStateSeq; + + void copyTo(@NonNull ChangeRecord changeRecord) { + changeRecord.isPending = isPending; + changeRecord.uid = uid; + changeRecord.change = change; + changeRecord.procState = procState; + changeRecord.capability = capability; + changeRecord.ephemeral = ephemeral; + changeRecord.procStateSeq = procStateSeq; + } + } + private static final class UidObserverRegistration { - final int mUid; - final String mPkg; - final int mWhich; - final int mCutpoint; + private final int mUid; + private final String mPkg; + private final int mWhich; + private final int mCutpoint; /** * Total # of callback calls that took more than {@link #SLOW_UID_OBSERVER_THRESHOLD_MS}. @@ -434,19 +434,51 @@ public class UidObserverController { ActivityManagerProto.UID_OBSERVER_FLAG_PROCSTATE, }; - UidObserverRegistration(int uid, String pkg, int which, int cutpoint) { + UidObserverRegistration(int uid, @NonNull String pkg, int which, int cutpoint) { this.mUid = uid; this.mPkg = pkg; this.mWhich = which; this.mCutpoint = cutpoint; - if (cutpoint >= ActivityManager.MIN_PROCESS_STATE) { - mLastProcStates = new SparseIntArray(); - } else { - mLastProcStates = null; + mLastProcStates = cutpoint >= ActivityManager.MIN_PROCESS_STATE + ? new SparseIntArray() : null; + } + + void dump(@NonNull PrintWriter pw, @NonNull IUidObserver observer) { + pw.print(" "); + UserHandle.formatUid(pw, mUid); + pw.print(" "); + pw.print(mPkg); + pw.print(" "); + pw.print(observer.getClass().getTypeName()); + pw.print(":"); + if ((mWhich & ActivityManager.UID_OBSERVER_IDLE) != 0) { + pw.print(" IDLE"); + } + if ((mWhich & ActivityManager.UID_OBSERVER_ACTIVE) != 0) { + pw.print(" ACT"); + } + if ((mWhich & ActivityManager.UID_OBSERVER_GONE) != 0) { + pw.print(" GONE"); + } + if ((mWhich & ActivityManager.UID_OBSERVER_PROCSTATE) != 0) { + pw.print(" STATE"); + pw.print(" (cut="); + pw.print(mCutpoint); + pw.print(")"); + } + pw.println(); + if (mLastProcStates != null) { + final int size = mLastProcStates.size(); + for (int j = 0; j < size; j++) { + pw.print(" Last "); + UserHandle.formatUid(pw, mLastProcStates.keyAt(j)); + pw.print(": "); + pw.println(mLastProcStates.valueAt(j)); + } } } - void dumpDebug(ProtoOutputStream proto, long fieldId) { + void dumpDebug(@NonNull ProtoOutputStream proto, long fieldId) { final long token = proto.start(fieldId); proto.write(UidObserverRegistrationProto.UID, mUid); proto.write(UidObserverRegistrationProto.PACKAGE, mPkg); diff --git a/services/core/java/com/android/server/am/UidRecord.java b/services/core/java/com/android/server/am/UidRecord.java index c84ccb298bef..f1945ede7237 100644 --- a/services/core/java/com/android/server/am/UidRecord.java +++ b/services/core/java/com/android/server/am/UidRecord.java @@ -27,6 +27,7 @@ import android.util.proto.ProtoOutputStream; import android.util.proto.ProtoUtils; import com.android.internal.annotations.GuardedBy; +import com.android.server.am.UidObserverController.ChangeRecord; /** * Overall information about a uid that has actively running processes. @@ -107,17 +108,9 @@ public final class UidRecord { UidRecordProto.CHANGE_UNCACHED, }; - static final class ChangeItem { - UidRecord uidRecord; - int uid; - int change; - int processState; - int capability; - boolean ephemeral; - long procStateSeq; - } + // UidObserverController is the only thing that should modify this. + final ChangeRecord pendingChange = new ChangeRecord(); - ChangeItem pendingChange; int lastReportedChange; public UidRecord(int _uid) { diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java index f1bad9819394..c29df6c44d5c 100644 --- a/services/core/java/com/android/server/am/UserController.java +++ b/services/core/java/com/android/server/am/UserController.java @@ -952,7 +952,7 @@ class UserController implements Handler.Callback { mInjector.batteryStatsServiceNoteEvent( BatteryStats.HistoryItem.EVENT_USER_RUNNING_FINISH, Integer.toString(userId), userId); - mInjector.getSystemServiceManager().stopUser(userId); + mInjector.getSystemServiceManager().onUserStopping(userId); Runnable finishUserStoppedAsync = () -> mHandler.post(() -> finishUserStopped(uss, allowDelayedLocking)); @@ -1028,7 +1028,7 @@ class UserController implements Handler.Callback { } if (stopped) { - mInjector.systemServiceManagerCleanupUser(userId); + mInjector.systemServiceManagerOnUserStopped(userId); mInjector.stackSupervisorRemoveUser(userId); // Remove the user if it is ephemeral. @@ -1307,7 +1307,7 @@ class UserController implements Handler.Callback { Slog.w(TAG, "No user info for user #" + userId); return false; } - if (foreground && userInfo.isManagedProfile()) { + if (foreground && userInfo.isProfile()) { Slog.w(TAG, "Cannot switch to User #" + userId + ": not a full user"); return false; } @@ -1612,7 +1612,7 @@ class UserController implements Handler.Callback { Slog.w(TAG, "Cannot switch to User #" + targetUserId + ": not supported"); return false; } - if (targetUserInfo.isManagedProfile()) { + if (targetUserInfo.isProfile()) { Slog.w(TAG, "Cannot switch to User #" + targetUserId + ": not a full user"); return false; } @@ -2494,8 +2494,8 @@ class UserController implements Handler.Callback { logUserLifecycleEvent(msg.arg1, USER_LIFECYCLE_EVENT_START_USER, USER_LIFECYCLE_EVENT_STATE_BEGIN); - mInjector.getSystemServiceManager().startUser(TimingsTraceAndSlog.newAsyncLog(), - msg.arg1); + mInjector.getSystemServiceManager().onUserStarting( + TimingsTraceAndSlog.newAsyncLog(), msg.arg1); logUserLifecycleEvent(msg.arg1, USER_LIFECYCLE_EVENT_START_USER, USER_LIFECYCLE_EVENT_STATE_FINISH); @@ -2503,7 +2503,7 @@ class UserController implements Handler.Callback { break; case USER_UNLOCK_MSG: final int userId = msg.arg1; - mInjector.getSystemServiceManager().unlockUser(userId); + mInjector.getSystemServiceManager().onUserUnlocking(userId); // Loads recents on a worker thread that allows disk I/O FgThread.getHandler().post(() -> { mInjector.loadUserRecents(userId); @@ -2528,7 +2528,7 @@ class UserController implements Handler.Callback { BatteryStats.HistoryItem.EVENT_USER_FOREGROUND_START, Integer.toString(msg.arg1), msg.arg1); - mInjector.getSystemServiceManager().switchUser(msg.arg2, msg.arg1); + mInjector.getSystemServiceManager().onUserSwitching(msg.arg2, msg.arg1); break; case FOREGROUND_PROFILE_CHANGED_MSG: dispatchForegroundProfileChanged(msg.arg1); @@ -2781,8 +2781,8 @@ class UserController implements Handler.Callback { LocalServices.getService(ActivityTaskManagerInternal.class).onUserStopped(userId); } - void systemServiceManagerCleanupUser(@UserIdInt int userId) { - mService.mSystemServiceManager.cleanupUser(userId); + void systemServiceManagerOnUserStopped(@UserIdInt int userId) { + mService.mSystemServiceManager.onUserStopped(userId); } protected UserManagerService getUserManager() { diff --git a/services/core/java/com/android/server/biometrics/sensors/BiometricUserState.java b/services/core/java/com/android/server/biometrics/sensors/BiometricUserState.java index 5b1834983345..a8250ac9e72d 100644 --- a/services/core/java/com/android/server/biometrics/sensors/BiometricUserState.java +++ b/services/core/java/com/android/server/biometrics/sensors/BiometricUserState.java @@ -16,6 +16,7 @@ package com.android.server.biometrics.sensors; +import android.annotation.NonNull; import android.content.Context; import android.hardware.biometrics.BiometricAuthenticator; import android.os.AsyncTask; @@ -62,11 +63,6 @@ public abstract class BiometricUserState<T extends BiometricAuthenticator.Identi protected abstract String getBiometricsTag(); /** - * @return The file where the biometric metadata should be stored. - */ - protected abstract String getBiometricFile(); - - /** * @return The resource for the name template, this is used to generate the default name. */ protected abstract int getNameTemplateResource(); @@ -88,8 +84,8 @@ public abstract class BiometricUserState<T extends BiometricAuthenticator.Identi throws IOException, XmlPullParserException; - public BiometricUserState(Context context, int userId) { - mFile = getFileForUser(userId); + public BiometricUserState(Context context, int userId, @NonNull String fileName) { + mFile = getFileForUser(userId, fileName); mContext = context; synchronized (this) { readStateSyncLocked(); @@ -159,8 +155,8 @@ public abstract class BiometricUserState<T extends BiometricAuthenticator.Identi return true; } - private File getFileForUser(int userId) { - return new File(Environment.getUserSystemDirectory(userId), getBiometricFile()); + private File getFileForUser(int userId, @NonNull String fileName) { + return new File(Environment.getUserSystemDirectory(userId), fileName); } private void scheduleWriteStateLocked() { diff --git a/services/core/java/com/android/server/biometrics/sensors/ClientMonitorCallbackConverter.java b/services/core/java/com/android/server/biometrics/sensors/ClientMonitorCallbackConverter.java index cb7db92ee132..c87f62f94499 100644 --- a/services/core/java/com/android/server/biometrics/sensors/ClientMonitorCallbackConverter.java +++ b/services/core/java/com/android/server/biometrics/sensors/ClientMonitorCallbackConverter.java @@ -89,7 +89,8 @@ public final class ClientMonitorCallbackConverter { } } - void onError(int sensorId, int cookie, int error, int vendorCode) throws RemoteException { + public void onError(int sensorId, int cookie, int error, int vendorCode) + throws RemoteException { if (mSensorReceiver != null) { mSensorReceiver.onError(sensorId, cookie, error, vendorCode); } else if (mFaceServiceReceiver != null) { diff --git a/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java b/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java index 5c08bce9b44f..e738d17f5f17 100644 --- a/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java @@ -57,7 +57,7 @@ public abstract class InternalCleanupClient<S extends BiometricAuthenticator.Ide } private final ArrayList<UserTemplate> mUnknownHALTemplates = new ArrayList<>(); - private final BiometricUtils mBiometricUtils; + private final BiometricUtils<S> mBiometricUtils; private final Map<Integer, Long> mAuthenticatorIds; private final List<S> mEnrolledList; private ClientMonitor<T> mCurrentTask; @@ -95,15 +95,15 @@ public abstract class InternalCleanupClient<S extends BiometricAuthenticator.Ide protected abstract InternalEnumerateClient<T> getEnumerateClient(Context context, LazyDaemon<T> lazyDaemon, IBinder token, int userId, String owner, - List<S> enrolledList, BiometricUtils utils, int sensorId); + List<S> enrolledList, BiometricUtils<S> utils, int sensorId); - protected abstract RemovalClient<T> getRemovalClient(Context context, LazyDaemon<T> lazyDaemon, - IBinder token, int biometricId, int userId, String owner, BiometricUtils utils, - int sensorId, Map<Integer, Long> authenticatorIds); + protected abstract RemovalClient<S, T> getRemovalClient(Context context, + LazyDaemon<T> lazyDaemon, IBinder token, int biometricId, int userId, String owner, + BiometricUtils<S> utils, int sensorId, Map<Integer, Long> authenticatorIds); protected InternalCleanupClient(@NonNull Context context, @NonNull LazyDaemon<T> lazyDaemon, int userId, @NonNull String owner, int sensorId, int statsModality, - @NonNull List<S> enrolledList, @NonNull BiometricUtils utils, + @NonNull List<S> enrolledList, @NonNull BiometricUtils<S> utils, @NonNull Map<Integer, Long> authenticatorIds) { super(context, lazyDaemon, null /* token */, null /* ClientMonitorCallbackConverter */, userId, owner, 0 /* cookie */, sensorId, statsModality, @@ -153,7 +153,7 @@ public abstract class InternalCleanupClient<S extends BiometricAuthenticator.Ide + mCurrentTask.getClass().getSimpleName()); return; } - ((RemovalClient<T>) mCurrentTask).onRemoved(identifier, remaining); + ((RemovalClient<S, T>) mCurrentTask).onRemoved(identifier, remaining); } @Override diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/AssistDataReceiver.java b/services/core/java/com/android/server/biometrics/sensors/LockoutConsumer.java index 7cd6c512b660..153bd4668a4f 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/AssistDataReceiver.java +++ b/services/core/java/com/android/server/biometrics/sensors/LockoutConsumer.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2017 The Android Open Source Project + * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -11,18 +11,15 @@ * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT 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 com.android.systemui.shared.system; - -import android.graphics.Bitmap; -import android.os.Bundle; +package com.android.server.biometrics.sensors; /** - * Abstract class for assist data receivers. + * Interface that clients interested/eligible for lockout events should implement. */ -public abstract class AssistDataReceiver { - public void onHandleAssistData(Bundle resultData) {} - public void onHandleAssistScreenshot(Bitmap screenshot) {} +public interface LockoutConsumer { + void onLockoutTimed(long durationMillis); + void onLockoutPermanent(); } diff --git a/services/core/java/com/android/server/biometrics/sensors/RemovalClient.java b/services/core/java/com/android/server/biometrics/sensors/RemovalClient.java index 1348f797a2dc..f79abd59dbb4 100644 --- a/services/core/java/com/android/server/biometrics/sensors/RemovalClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/RemovalClient.java @@ -17,6 +17,7 @@ package com.android.server.biometrics.sensors; import android.annotation.NonNull; +import android.annotation.Nullable; import android.content.Context; import android.hardware.biometrics.BiometricAuthenticator; import android.hardware.biometrics.BiometricsProtoEnums; @@ -29,17 +30,18 @@ import java.util.Map; /** * A class to keep track of the remove state for a given client. */ -public abstract class RemovalClient<T> extends ClientMonitor<T> implements RemovalConsumer { +public abstract class RemovalClient<S extends BiometricAuthenticator.Identifier, T> + extends ClientMonitor<T> implements RemovalConsumer { private static final String TAG = "Biometrics/RemovalClient"; protected final int mBiometricId; - private final BiometricUtils mBiometricUtils; + private final BiometricUtils<S> mBiometricUtils; private final Map<Integer, Long> mAuthenticatorIds; public RemovalClient(@NonNull Context context, @NonNull LazyDaemon<T> lazyDaemon, @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, - int biometricId, int userId, @NonNull String owner, @NonNull BiometricUtils utils, + int biometricId, int userId, @NonNull String owner, @NonNull BiometricUtils<S> utils, int sensorId, @NonNull Map<Integer, Long> authenticatorIds, int statsModality) { super(context, lazyDaemon, token, listener, userId, owner, 0 /* cookie */, sensorId, statsModality, BiometricsProtoEnums.ACTION_REMOVE, @@ -63,8 +65,8 @@ public abstract class RemovalClient<T> extends ClientMonitor<T> implements Remov } @Override - public void onRemoved(BiometricAuthenticator.Identifier identifier, int remaining) { - if (identifier.getBiometricId() != 0) { + public void onRemoved(@Nullable BiometricAuthenticator.Identifier identifier, int remaining) { + if (identifier != null) { mBiometricUtils.removeBiometricForUser(getContext(), getTargetUserId(), identifier.getBiometricId()); } diff --git a/services/core/java/com/android/server/biometrics/sensors/RemovalConsumer.java b/services/core/java/com/android/server/biometrics/sensors/RemovalConsumer.java index 3d7988cf138f..0aba7e420cfb 100644 --- a/services/core/java/com/android/server/biometrics/sensors/RemovalConsumer.java +++ b/services/core/java/com/android/server/biometrics/sensors/RemovalConsumer.java @@ -16,6 +16,7 @@ package com.android.server.biometrics.sensors; +import android.annotation.Nullable; import android.hardware.biometrics.BiometricAuthenticator; /** @@ -28,5 +29,5 @@ public interface RemovalConsumer { * @param remaining number of templates that still need to be removed before the operation in * the HAL is complete (e.g. when removing all templates). */ - void onRemoved(BiometricAuthenticator.Identifier identifier, int remaining); + void onRemoved(@Nullable BiometricAuthenticator.Identifier identifier, int remaining); } diff --git a/services/core/java/com/android/server/biometrics/sensors/face/Face10.java b/services/core/java/com/android/server/biometrics/sensors/face/Face10.java index c2d4c152cba6..f7998ee8caeb 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/Face10.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/Face10.java @@ -219,8 +219,7 @@ class Face10 implements IHwBinder.DeathRecipient { removalConsumer.onRemoved(face, remaining); } } else { - final Face face = new Face("", 0 /* identifier */, deviceId); - removalConsumer.onRemoved(face, 0 /* remaining */); + removalConsumer.onRemoved(null, 0 /* remaining */); } Settings.Secure.putIntForUser(mContext.getContentResolver(), diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceEnrollClient.java index 3e843cae96fd..989b5c938cbc 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/FaceEnrollClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceEnrollClient.java @@ -23,6 +23,7 @@ import android.hardware.biometrics.BiometricFaceConstants; import android.hardware.biometrics.BiometricsProtoEnums; import android.hardware.biometrics.face.V1_0.IBiometricsFace; import android.hardware.biometrics.face.V1_0.Status; +import android.hardware.face.Face; import android.hardware.face.FaceManager; import android.os.IBinder; import android.os.NativeHandle; @@ -53,9 +54,9 @@ public class FaceEnrollClient extends EnrollClient<IBiometricsFace> { FaceEnrollClient(@NonNull Context context, @NonNull LazyDaemon<IBiometricsFace> lazyDaemon, @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, int userId, - @NonNull byte[] hardwareAuthToken, @NonNull String owner, @NonNull BiometricUtils utils, - @NonNull int[] disabledFeatures, int timeoutSec, @Nullable NativeHandle surfaceHandle, - int sensorId) { + @NonNull byte[] hardwareAuthToken, @NonNull String owner, + @NonNull BiometricUtils<Face> utils, @NonNull int[] disabledFeatures, int timeoutSec, + @Nullable NativeHandle surfaceHandle, int sensorId) { super(context, lazyDaemon, token, listener, userId, hardwareAuthToken, owner, utils, timeoutSec, BiometricsProtoEnums.MODALITY_FACE, sensorId, false /* shouldVibrate */); diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceInternalCleanupClient.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceInternalCleanupClient.java index 93f35f4c90cd..7626587f5519 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/FaceInternalCleanupClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceInternalCleanupClient.java @@ -40,7 +40,7 @@ class FaceInternalCleanupClient extends InternalCleanupClient<Face, IBiometricsF FaceInternalCleanupClient(@NonNull Context context, @NonNull LazyDaemon<IBiometricsFace> lazyDaemon, int userId, @NonNull String owner, - int sensorId, @NonNull List<Face> enrolledList, @NonNull BiometricUtils utils, + int sensorId, @NonNull List<Face> enrolledList, @NonNull BiometricUtils<Face> utils, @NonNull Map<Integer, Long> authenticatorIds) { super(context, lazyDaemon, userId, owner, sensorId, BiometricsProtoEnums.MODALITY_FACE, enrolledList, utils, authenticatorIds); @@ -49,15 +49,15 @@ class FaceInternalCleanupClient extends InternalCleanupClient<Face, IBiometricsF @Override protected InternalEnumerateClient<IBiometricsFace> getEnumerateClient(Context context, LazyDaemon<IBiometricsFace> lazyDaemon, IBinder token, int userId, String owner, - List<Face> enrolledList, BiometricUtils utils, int sensorId) { + List<Face> enrolledList, BiometricUtils<Face> utils, int sensorId) { return new FaceInternalEnumerateClient(context, lazyDaemon, token, userId, owner, enrolledList, utils, sensorId); } @Override - protected RemovalClient<IBiometricsFace> getRemovalClient(Context context, + protected RemovalClient<Face, IBiometricsFace> getRemovalClient(Context context, LazyDaemon<IBiometricsFace> lazyDaemon, IBinder token, - int biometricId, int userId, String owner, BiometricUtils utils, int sensorId, + int biometricId, int userId, String owner, BiometricUtils<Face> utils, int sensorId, Map<Integer, Long> authenticatorIds) { // Internal remove does not need to send results to anyone. Cleanup (enumerate + remove) // is all done internally. diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceInternalEnumerateClient.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceInternalEnumerateClient.java index 32d48321428a..4166dff85332 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/FaceInternalEnumerateClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceInternalEnumerateClient.java @@ -40,8 +40,8 @@ class FaceInternalEnumerateClient extends InternalEnumerateClient<IBiometricsFac FaceInternalEnumerateClient(@NonNull Context context, @NonNull LazyDaemon<IBiometricsFace> lazyDaemon, @NonNull IBinder token, int userId, - @NonNull String owner, @NonNull List<Face> enrolledList, @NonNull BiometricUtils utils, - int sensorId) { + @NonNull String owner, @NonNull List<Face> enrolledList, + @NonNull BiometricUtils<Face> utils, int sensorId) { super(context, lazyDaemon, token, userId, owner, enrolledList, utils, sensorId, BiometricsProtoEnums.MODALITY_FACE); } diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceRemovalClient.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceRemovalClient.java index dde5ababd6c0..31ae3a387f90 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/FaceRemovalClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceRemovalClient.java @@ -20,6 +20,7 @@ import android.annotation.NonNull; import android.content.Context; import android.hardware.biometrics.BiometricsProtoEnums; import android.hardware.biometrics.face.V1_0.IBiometricsFace; +import android.hardware.face.Face; import android.os.IBinder; import android.os.RemoteException; import android.util.Slog; @@ -34,12 +35,12 @@ import java.util.Map; * Face-specific removal client supporting the {@link android.hardware.biometrics.face.V1_0} * and {@link android.hardware.biometrics.face.V1_1} HIDL interfaces. */ -class FaceRemovalClient extends RemovalClient<IBiometricsFace> { +class FaceRemovalClient extends RemovalClient<Face, IBiometricsFace> { private static final String TAG = "FaceRemovalClient"; FaceRemovalClient(@NonNull Context context, @NonNull LazyDaemon<IBiometricsFace> lazyDaemon, @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, - int biometricId, int userId, @NonNull String owner, @NonNull BiometricUtils utils, + int biometricId, int userId, @NonNull String owner, @NonNull BiometricUtils<Face> utils, int sensorId, @NonNull Map<Integer, Long> authenticatorIds) { super(context, lazyDaemon, token, listener, biometricId, userId, owner, utils, sensorId, authenticatorIds, BiometricsProtoEnums.MODALITY_FACE); diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceUserState.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceUserState.java index 42c7d16951d7..d30c3c804362 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/FaceUserState.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceUserState.java @@ -17,7 +17,6 @@ package com.android.server.biometrics.sensors.face; import android.content.Context; -import android.hardware.biometrics.BiometricAuthenticator; import android.hardware.face.Face; import android.util.AtomicFile; import android.util.Slog; @@ -41,10 +40,9 @@ import java.util.ArrayList; * Class managing the set of faces per user across device reboots. * @hide */ -public class FaceUserState extends BiometricUserState { +public class FaceUserState extends BiometricUserState<Face> { private static final String TAG = "FaceState"; - private static final String FACE_FILE = "settings_face.xml"; private static final String TAG_FACES = "faces"; private static final String TAG_FACE = "face"; @@ -52,8 +50,8 @@ public class FaceUserState extends BiometricUserState { private static final String ATTR_FACE_ID = "faceId"; private static final String ATTR_DEVICE_ID = "deviceId"; - public FaceUserState(Context ctx, int userId) { - super(ctx, userId); + public FaceUserState(Context ctx, int userId, String fileName) { + super(ctx, userId, fileName); } @Override @@ -62,29 +60,14 @@ public class FaceUserState extends BiometricUserState { } @Override - protected String getBiometricFile() { - return FACE_FILE; - } - - @Override protected int getNameTemplateResource() { return com.android.internal.R.string.face_name_template; } @Override - public void addBiometric(BiometricAuthenticator.Identifier identifier) { - if (identifier instanceof Face) { - super.addBiometric(identifier); - } else { - Slog.w(TAG, "Attempted to add non-face identifier"); - } - } - - @Override - protected ArrayList getCopy(ArrayList array) { - ArrayList<Face> result = new ArrayList<>(array.size()); - for (int i = 0; i < array.size(); i++) { - Face f = (Face) array.get(i); + protected ArrayList<Face> getCopy(ArrayList<Face> array) { + final ArrayList<Face> result = new ArrayList<>(); + for (Face f : array) { result.add(new Face(f.getName(), f.getBiometricId(), f.getDeviceId())); } return result; diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceUtils.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceUtils.java index 0197028cfd6e..a0ffe58c779b 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/FaceUtils.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceUtils.java @@ -16,6 +16,7 @@ package com.android.server.biometrics.sensors.face; +import android.annotation.Nullable; import android.content.Context; import android.hardware.biometrics.BiometricAuthenticator; import android.hardware.face.Face; @@ -30,24 +31,59 @@ import java.util.List; /** * Utility class for dealing with faces and face settings. */ -public class FaceUtils implements BiometricUtils { +public class FaceUtils implements BiometricUtils<Face> { private static final Object sInstanceLock = new Object(); - private static FaceUtils sInstance; + // Map<SensorId, FaceUtils> + private static SparseArray<FaceUtils> sInstances; + private static final String LEGACY_FACE_FILE = "settings_face.xml"; @GuardedBy("this") - private final SparseArray<FaceUserState> mUsers = new SparseArray<>(); + private final SparseArray<FaceUserState> mUserStates; + private final String mFileName; - public static FaceUtils getInstance() { + public static FaceUtils getInstance(int sensorId) { + // Specify a null fileName to use an auto-generated sensorId-specific filename. + return getInstance(sensorId, null /* fileName */); + } + + /** + * Retrieves an instance for the specified sensorId. If the fileName is null, a default + * filename (e.g. settings_face_<sensorId>.xml will be generated. + * + * Specifying an explicit fileName allows for backward compatibility with legacy devices, + * where everything is stored in settings_face.xml. + */ + private static FaceUtils getInstance(int sensorId, @Nullable String fileName) { + final FaceUtils utils; synchronized (sInstanceLock) { - if (sInstance == null) { - sInstance = new FaceUtils(); + if (sInstances == null) { + sInstances = new SparseArray<>(); + } + if (sInstances.get(sensorId) == null) { + if (fileName == null) { + fileName = "settings_face_" + sensorId + ".xml"; + } + sInstances.put(sensorId, new FaceUtils(fileName)); } + utils = sInstances.get(sensorId); } - return sInstance; + return utils; + } + + /** + * Legacy getter for {@link android.hardware.biometrics.face.V1_0} and its extended subclasses, + * which do not support a well defined sensorId from the HAL. + */ + public static FaceUtils getInstance() { + // Note that sensorId for legacy services can be hard-coded to 0 since it's only used + // to index into the sensor states map. + return getInstance(0 /* sensorId */, LEGACY_FACE_FILE); } - private FaceUtils() { + private FaceUtils(String fileName) { + mUserStates = new SparseArray<>(); + mFileName = fileName; } @Override @@ -56,9 +92,8 @@ public class FaceUtils implements BiometricUtils { } @Override - public void addBiometricForUser(Context ctx, int userId, - BiometricAuthenticator.Identifier identifier) { - getStateForUser(ctx, userId).addBiometric(identifier); + public void addBiometricForUser(Context ctx, int userId, Face face) { + getStateForUser(ctx, userId).addBiometric(face); } @Override @@ -82,10 +117,10 @@ public class FaceUtils implements BiometricUtils { private FaceUserState getStateForUser(Context ctx, int userId) { synchronized (this) { - FaceUserState state = mUsers.get(userId); + FaceUserState state = mUserStates.get(userId); if (state == null) { - state = new FaceUserState(ctx, userId); - mUsers.put(userId, state); + state = new FaceUserState(ctx, userId, mFileName); + mUserStates.put(userId, state); } return state; } diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java index 45de538c62d9..5dcadee20e13 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java @@ -33,10 +33,9 @@ import android.content.pm.PackageManager; import android.hardware.biometrics.BiometricsProtoEnums; import android.hardware.biometrics.IBiometricSensorReceiver; import android.hardware.biometrics.IBiometricServiceLockoutResetCallback; +import android.hardware.biometrics.ITestSession; import android.hardware.biometrics.fingerprint.IFingerprint; import android.hardware.biometrics.fingerprint.SensorProps; -import android.hardware.biometrics.ITestService; -import android.hardware.biometrics.SensorPropertiesInternal; import android.hardware.fingerprint.Fingerprint; import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; import android.hardware.fingerprint.IFingerprintClientActiveCallback; @@ -53,6 +52,8 @@ import android.os.RemoteException; import android.os.ServiceManager; import android.os.UserHandle; import android.provider.Settings; +import android.util.ArrayMap; +import android.util.ArraySet; import android.util.EventLog; import android.util.Pair; import android.util.Slog; @@ -91,54 +92,52 @@ public class FingerprintService extends SystemService { private final GestureAvailabilityDispatcher mGestureAvailabilityDispatcher; private final LockPatternUtils mLockPatternUtils; @NonNull private List<ServiceProvider> mServiceProviders; - @Nullable private TestService mTestService; + @NonNull private final ArrayMap<Integer, TestSession> mTestSessions; - private final class TestService extends ITestService.Stub { + private final class TestSession extends ITestSession.Stub { + private final int mSensorId; - @Override - public List<SensorPropertiesInternal> getSensorPropertiesInternal( - String opPackageName) { - Utils.checkPermission(getContext(), TEST_BIOMETRIC); - return null; + TestSession(int sensorId) { + mSensorId = sensorId; } @Override - public void enableTestHal(int sensorId, boolean enableTestHal) { + public void enableTestHal(boolean enableTestHal) { Utils.checkPermission(getContext(), TEST_BIOMETRIC); } @Override - public void enrollStart(int sensorId, int userId) { + public void startEnroll(int userId) { Utils.checkPermission(getContext(), TEST_BIOMETRIC); } @Override - public void enrollFinish(int sensorId, int userId) { + public void finishEnroll(int userId) { Utils.checkPermission(getContext(), TEST_BIOMETRIC); } @Override - public void authenticateSuccess(int sensorId, int userId) { + public void acceptAuthentication(int userId) { Utils.checkPermission(getContext(), TEST_BIOMETRIC); } @Override - public void authenticateReject(int sensorId, int userId) { + public void rejectAuthentication(int userId) { Utils.checkPermission(getContext(), TEST_BIOMETRIC); } @Override - public void notifyAcquired(int sensorId, int userId) { + public void notifyAcquired(int userId, int acquireInfo) { Utils.checkPermission(getContext(), TEST_BIOMETRIC); } @Override - public void notifyError(int sensorId, int userId) { + public void notifyError(int userId, int errorCode) { Utils.checkPermission(getContext(), TEST_BIOMETRIC); } @Override - public void internalCleanup(int sensorId, int userId) { + public void cleanupInternalState(int userId) { Utils.checkPermission(getContext(), TEST_BIOMETRIC); } } @@ -148,15 +147,17 @@ public class FingerprintService extends SystemService { */ private final class FingerprintServiceWrapper extends IFingerprintService.Stub { @Override - public ITestService getTestService(String opPackageName) { + public ITestSession createTestSession(int sensorId, String opPackageName) { Utils.checkPermission(getContext(), TEST_BIOMETRIC); - synchronized (this) { - if (mTestService == null) { - mTestService = new TestService(); + final TestSession session; + synchronized (mTestSessions) { + if (!mTestSessions.containsKey(sensorId)) { + mTestSessions.put(sensorId, new TestSession(sensorId)); } + session = mTestSessions.get(sensorId); } - return mTestService; + return session; } @Override // Binder call @@ -173,7 +174,7 @@ public class FingerprintService extends SystemService { } @Override // Binder call - public void generateChallenge(IBinder token, int sensorId, + public void generateChallenge(IBinder token, int sensorId, int userId, IFingerprintServiceReceiver receiver, String opPackageName) { Utils.checkPermission(getContext(), MANAGE_FINGERPRINT); @@ -183,20 +184,21 @@ public class FingerprintService extends SystemService { return; } - provider.scheduleGenerateChallenge(sensorId, token, receiver, opPackageName); + provider.scheduleGenerateChallenge(sensorId, userId, token, receiver, opPackageName); } @Override // Binder call - public void revokeChallenge(IBinder token, String opPackageName, long challenge) { + public void revokeChallenge(IBinder token, int sensorId, int userId, String opPackageName, + long challenge) { Utils.checkPermission(getContext(), MANAGE_FINGERPRINT); - final Pair<Integer, ServiceProvider> provider = getSingleProvider(); + final ServiceProvider provider = getProviderForSensor(sensorId); if (provider == null) { - Slog.w(TAG, "Null provider for revokeChallenge"); + Slog.w(TAG, "No matching sensor for revokeChallenge, sensorId: " + sensorId); return; } - provider.second.scheduleRevokeChallenge(provider.first, token, opPackageName, + provider.scheduleRevokeChallenge(sensorId, userId, token, opPackageName, challenge); } @@ -620,6 +622,7 @@ public class FingerprintService extends SystemService { mLockoutResetDispatcher = new LockoutResetDispatcher(context); mLockPatternUtils = new LockPatternUtils(context); mServiceProviders = new ArrayList<>(); + mTestSessions = new ArrayMap<>(); initializeAidlHals(); } diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintUserState.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintUserState.java index 56312bcb16e4..e56c8d5b5ceb 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintUserState.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintUserState.java @@ -17,7 +17,6 @@ package com.android.server.biometrics.sensors.fingerprint; import android.content.Context; -import android.hardware.biometrics.BiometricAuthenticator; import android.hardware.fingerprint.Fingerprint; import android.util.AtomicFile; import android.util.Slog; @@ -40,10 +39,9 @@ import java.util.ArrayList; * Class managing the set of fingerprint per user across device reboots. * @hide */ -public class FingerprintUserState extends BiometricUserState { +public class FingerprintUserState extends BiometricUserState<Fingerprint> { private static final String TAG = "FingerprintState"; - private static final String FINGERPRINT_FILE = "settings_fingerprint.xml"; private static final String TAG_FINGERPRINTS = "fingerprints"; private static final String TAG_FINGERPRINT = "fingerprint"; @@ -52,8 +50,8 @@ public class FingerprintUserState extends BiometricUserState { private static final String ATTR_FINGER_ID = "fingerId"; private static final String ATTR_DEVICE_ID = "deviceId"; - public FingerprintUserState(Context context, int userId) { - super(context, userId); + public FingerprintUserState(Context context, int userId, String fileName) { + super(context, userId, fileName); } @Override @@ -62,29 +60,14 @@ public class FingerprintUserState extends BiometricUserState { } @Override - protected String getBiometricFile() { - return FINGERPRINT_FILE; - } - - @Override protected int getNameTemplateResource() { return com.android.internal.R.string.fingerprint_name_template; } @Override - public void addBiometric(BiometricAuthenticator.Identifier identifier) { - if (identifier instanceof Fingerprint) { - super.addBiometric(identifier); - } else { - Slog.w(TAG, "Attempted to add non-fingerprint identifier"); - } - } - - @Override - protected ArrayList getCopy(ArrayList array) { - ArrayList<Fingerprint> result = new ArrayList<>(); - for (int i = 0; i < array.size(); i++) { - Fingerprint fp = (Fingerprint) array.get(i); + protected ArrayList<Fingerprint> getCopy(ArrayList<Fingerprint> array) { + final ArrayList<Fingerprint> result = new ArrayList<>(); + for (Fingerprint fp : array) { result.add(new Fingerprint(fp.getName(), fp.getGroupId(), fp.getBiometricId(), fp.getDeviceId())); } diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintUtils.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintUtils.java index f0bfe12db32a..6da86502b64c 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintUtils.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintUtils.java @@ -16,8 +16,8 @@ package com.android.server.biometrics.sensors.fingerprint; +import android.annotation.Nullable; import android.content.Context; -import android.hardware.biometrics.BiometricAuthenticator; import android.hardware.fingerprint.Fingerprint; import android.text.TextUtils; import android.util.SparseArray; @@ -30,24 +30,62 @@ import java.util.List; /** * Utility class for dealing with fingerprints and fingerprint settings. */ -public class FingerprintUtils implements BiometricUtils { +public class FingerprintUtils implements BiometricUtils<Fingerprint> { private static final Object sInstanceLock = new Object(); - private static FingerprintUtils sInstance; + // Map<SensorId, FingerprintUtils> + private static SparseArray<FingerprintUtils> sInstances; + private static final String LEGACY_FINGERPRINT_FILE = "settings_fingerprint.xml"; @GuardedBy("this") - private final SparseArray<FingerprintUserState> mUsers = new SparseArray<>(); + private final SparseArray<FingerprintUserState> mUserStates; + private final String mFileName; + + /** + * Retrieves an instance for the specified sensorId. + */ + public static FingerprintUtils getInstance(int sensorId) { + // Specify a null fileName to use an auto-generated sensorId-specific filename. + return getInstance(sensorId, null /* fileName */); + } - public static FingerprintUtils getInstance() { + /** + * Retrieves an instance for the specified sensorId. If the fileName is null, a default + * filename (e.g. settings_fingerprint_<sensorId>.xml will be generated. + * + * Specifying an explicit fileName allows for backward compatibility with legacy devices, + * where everything is stored in settings_fingerprint.xml. + */ + private static FingerprintUtils getInstance(int sensorId, @Nullable String fileName) { + final FingerprintUtils utils; synchronized (sInstanceLock) { - if (sInstance == null) { - sInstance = new FingerprintUtils(); + if (sInstances == null) { + sInstances = new SparseArray<>(); + } + if (sInstances.get(sensorId) == null) { + if (fileName == null) { + fileName = "settings_fingerprint_" + sensorId + ".xml"; + } + sInstances.put(sensorId, new FingerprintUtils(fileName)); } + utils = sInstances.get(sensorId); } - return sInstance; + return utils; + } + + /** + * Legacy getter for {@link android.hardware.biometrics.fingerprint.V2_1} ands its extended + * subclasses, which do not support a well defined sensorId from the HAL. + */ + public static FingerprintUtils getInstance() { + // Note that sensorId for legacy services can be hard-coded to 0 since it's only used + // to index into the sensor states map. + return getInstance(0 /* sensorId */, LEGACY_FINGERPRINT_FILE); } - private FingerprintUtils() { + private FingerprintUtils(String fileName) { + mUserStates = new SparseArray<>(); + mFileName = fileName; } @Override @@ -56,9 +94,8 @@ public class FingerprintUtils implements BiometricUtils { } @Override - public void addBiometricForUser(Context context, int userId, - BiometricAuthenticator.Identifier identifier) { - getStateForUser(context, userId).addBiometric(identifier); + public void addBiometricForUser(Context context, int userId, Fingerprint fingerprint) { + getStateForUser(context, userId).addBiometric(fingerprint); } @Override @@ -83,10 +120,10 @@ public class FingerprintUtils implements BiometricUtils { private FingerprintUserState getStateForUser(Context ctx, int userId) { synchronized (this) { - FingerprintUserState state = mUsers.get(userId); + FingerprintUserState state = mUserStates.get(userId); if (state == null) { - state = new FingerprintUserState(ctx, userId); - mUsers.put(userId, state); + state = new FingerprintUserState(ctx, userId, mFileName); + mUserStates.put(userId, state); } return state; } diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java index 35d01882f0c6..c2315fdd4ccc 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java @@ -64,10 +64,10 @@ public interface ServiceProvider { void scheduleResetLockout(int sensorId, int userId, @Nullable byte[] hardwareAuthToken); - void scheduleGenerateChallenge(int sensorId, @NonNull IBinder token, + void scheduleGenerateChallenge(int sensorId, int userId, @NonNull IBinder token, @NonNull IFingerprintServiceReceiver receiver, String opPackageName); - void scheduleRevokeChallenge(int sensorId, @NonNull IBinder token, + void scheduleRevokeChallenge(int sensorId, int userId, @NonNull IBinder token, @NonNull String opPackageName, long challenge); void scheduleEnroll(int sensorId, @NonNull IBinder token, byte[] hardwareAuthToken, int userId, diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java new file mode 100644 index 000000000000..8acf4f5204a9 --- /dev/null +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java @@ -0,0 +1,152 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.biometrics.sensors.fingerprint.aidl; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.app.TaskStackListener; +import android.content.Context; +import android.hardware.biometrics.BiometricAuthenticator; +import android.hardware.biometrics.BiometricFingerprintConstants; +import android.hardware.biometrics.BiometricsProtoEnums; +import android.hardware.biometrics.common.ICancellationSignal; +import android.hardware.biometrics.fingerprint.ISession; +import android.hardware.fingerprint.IUdfpsOverlayController; +import android.os.IBinder; +import android.os.RemoteException; +import android.util.Slog; + +import com.android.server.biometrics.sensors.AuthenticationClient; +import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter; +import com.android.server.biometrics.sensors.LockoutConsumer; +import com.android.server.biometrics.sensors.LockoutTracker; +import com.android.server.biometrics.sensors.fingerprint.Udfps; +import com.android.server.biometrics.sensors.fingerprint.UdfpsHelper; + +import java.util.ArrayList; + +/** + * Fingerprint-specific authentication client supporting the + * {@link android.hardware.biometrics.fingerprint.IFingerprint} AIDL interface. + */ +class FingerprintAuthenticationClient extends AuthenticationClient<ISession> implements + Udfps, LockoutConsumer { + private static final String TAG = "FingerprintAuthenticationClient"; + + @NonNull private final LockoutCache mLockoutCache; + @Nullable private final IUdfpsOverlayController mUdfpsOverlayController; + @Nullable private ICancellationSignal mCancellationSignal; + + FingerprintAuthenticationClient(@NonNull Context context, + @NonNull LazyDaemon<ISession> lazyDaemon, @NonNull IBinder token, + @NonNull ClientMonitorCallbackConverter listener, int targetUserId, long operationId, + boolean restricted, @NonNull String owner, int cookie, boolean requireConfirmation, + int sensorId, boolean isStrongBiometric, int statsClient, + @Nullable TaskStackListener taskStackListener, @NonNull LockoutCache lockoutCache, + @Nullable IUdfpsOverlayController udfpsOverlayController) { + super(context, lazyDaemon, token, listener, targetUserId, operationId, restricted, owner, + cookie, requireConfirmation, sensorId, isStrongBiometric, + BiometricsProtoEnums.MODALITY_FINGERPRINT, statsClient, taskStackListener, + lockoutCache); + mLockoutCache = lockoutCache; + mUdfpsOverlayController = udfpsOverlayController; + } + + @Override + public void onAuthenticated(BiometricAuthenticator.Identifier identifier, + boolean authenticated, ArrayList<Byte> token) { + super.onAuthenticated(identifier, authenticated, token); + + if (authenticated) { + UdfpsHelper.hideUdfpsOverlay(getSensorId(), mUdfpsOverlayController); + mCallback.onClientFinished(this, true /* success */); + } + } + + @Override + protected void startHalOperation() { + UdfpsHelper.showUdfpsOverlay(getSensorId(), mUdfpsOverlayController); + try { + mCancellationSignal = getFreshDaemon().authenticate(mSequentialId, mOperationId); + } catch (RemoteException e) { + Slog.e(TAG, "Remote exception", e); + onError(BiometricFingerprintConstants.FINGERPRINT_ERROR_HW_UNAVAILABLE, + 0 /* vendorCode */); + UdfpsHelper.hideUdfpsOverlay(getSensorId(), mUdfpsOverlayController); + mCallback.onClientFinished(this, false /* success */); + } + } + + @Override + protected void stopHalOperation() { + UdfpsHelper.hideUdfpsOverlay(getSensorId(), mUdfpsOverlayController); + try { + mCancellationSignal.cancel(); + } catch (RemoteException e) { + Slog.e(TAG, "Remote exception", e); + onError(BiometricFingerprintConstants.FINGERPRINT_ERROR_HW_UNAVAILABLE, + 0 /* vendorCode */); + mCallback.onClientFinished(this, false /* success */); + } + } + + @Override + public void onPointerDown(int x, int y, float minor, float major) { + try { + getFreshDaemon().onPointerDown(0 /* pointerId */, x, y, minor, major); + } catch (RemoteException e) { + Slog.e(TAG, "Remote exception", e); + } + } + + @Override + public void onPointerUp() { + try { + getFreshDaemon().onPointerUp(0 /* pointerId */); + } catch (RemoteException e) { + Slog.e(TAG, "Remote exception", e); + } + } + + @Override + public void onLockoutTimed(long durationMillis) { + mLockoutCache.setLockoutModeForUser(getTargetUserId(), LockoutTracker.LOCKOUT_TIMED); + // Lockout metrics are logged as an error code. + final int error = BiometricFingerprintConstants.FINGERPRINT_ERROR_LOCKOUT; + logOnError(getContext(), error, 0 /* vendorCode */, getTargetUserId()); + + try { + getListener().onError(getSensorId(), getCookie(), error, 0 /* vendorCode */); + } catch (RemoteException e) { + Slog.e(TAG, "Remote exception", e); + } + } + + @Override + public void onLockoutPermanent() { + mLockoutCache.setLockoutModeForUser(getTargetUserId(), LockoutTracker.LOCKOUT_PERMANENT); + // Lockout metrics are logged as an error code. + final int error = BiometricFingerprintConstants.FINGERPRINT_ERROR_LOCKOUT_PERMANENT; + logOnError(getContext(), error, 0 /* vendorCode */, getTargetUserId()); + + try { + getListener().onError(getSensorId(), getCookie(), error, 0 /* vendorCode */); + } catch (RemoteException e) { + Slog.e(TAG, "Remote exception", e); + } + } +} diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java new file mode 100644 index 000000000000..339832373b74 --- /dev/null +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintDetectClient.java @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.biometrics.sensors.fingerprint.aidl; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.Context; +import android.hardware.biometrics.BiometricsProtoEnums; +import android.hardware.biometrics.common.ICancellationSignal; +import android.hardware.biometrics.fingerprint.ISession; +import android.hardware.fingerprint.IUdfpsOverlayController; +import android.os.IBinder; +import android.os.RemoteException; +import android.util.Slog; + +import com.android.server.biometrics.sensors.AcquisitionClient; +import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter; +import com.android.server.biometrics.sensors.fingerprint.UdfpsHelper; + +/** + * Performs fingerprint detection without exposing any matching information (e.g. accept/reject + * have the same haptic, lockout counter is not increased). + */ +class FingerprintDetectClient extends AcquisitionClient<ISession> { + + private static final String TAG = "FingerprintDetectClient"; + + private final boolean mIsStrongBiometric; + @Nullable private final IUdfpsOverlayController mUdfpsOverlayController; + + @Nullable private ICancellationSignal mCancellationSignal; + + FingerprintDetectClient(@NonNull Context context, @NonNull LazyDaemon<ISession> lazyDaemon, + @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, int userId, + @NonNull String owner, int sensorId, + @Nullable IUdfpsOverlayController udfpsOverlayController, boolean isStrongBiometric, + int statsClient) { + super(context, lazyDaemon, token, listener, userId, owner, 0 /* cookie */, sensorId, + BiometricsProtoEnums.MODALITY_FINGERPRINT, BiometricsProtoEnums.ACTION_AUTHENTICATE, + statsClient); + mIsStrongBiometric = isStrongBiometric; + mUdfpsOverlayController = udfpsOverlayController; + } + + @Override + protected void stopHalOperation() { + UdfpsHelper.hideUdfpsOverlay(getSensorId(), mUdfpsOverlayController); + try { + mCancellationSignal.cancel(); + } catch (RemoteException e) { + Slog.e(TAG, "Remote exception", e); + mCallback.onClientFinished(this, false /* success */); + } + } + + @Override + protected void startHalOperation() { + UdfpsHelper.showUdfpsOverlay(getSensorId(), mUdfpsOverlayController); + try { + mCancellationSignal = getFreshDaemon().detectInteraction(mSequentialId); + } catch (RemoteException e) { + Slog.e(TAG, "Remote exception when requesting finger detect", e); + UdfpsHelper.hideUdfpsOverlay(getSensorId(), mUdfpsOverlayController); + mCallback.onClientFinished(this, false /* success */); + } + } + + void onInteractionDetected() { + vibrateSuccess(); + + try { + getListener().onDetected(getSensorId(), getTargetUserId(), mIsStrongBiometric); + mCallback.onClientFinished(this, true /* success */); + } catch (RemoteException e) { + Slog.e(TAG, "Remote exception when sending onDetected", e); + mCallback.onClientFinished(this, false /* success */); + } + } +} diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java index 33f5418ee620..437ecd7a0f8d 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClient.java @@ -21,10 +21,11 @@ import android.annotation.Nullable; import android.content.Context; import android.hardware.biometrics.BiometricFaceConstants; import android.hardware.biometrics.BiometricFingerprintConstants; +import android.hardware.biometrics.BiometricsProtoEnums; import android.hardware.biometrics.common.ICancellationSignal; import android.hardware.biometrics.fingerprint.ISession; +import android.hardware.fingerprint.Fingerprint; import android.hardware.fingerprint.IUdfpsOverlayController; -import android.hardware.keymaster.HardwareAuthToken; import android.os.IBinder; import android.os.RemoteException; import android.util.Slog; @@ -37,7 +38,7 @@ import com.android.server.biometrics.sensors.fingerprint.FingerprintUtils; import com.android.server.biometrics.sensors.fingerprint.Udfps; import com.android.server.biometrics.sensors.fingerprint.UdfpsHelper; -public class FingerprintEnrollClient extends EnrollClient<ISession> implements Udfps { +class FingerprintEnrollClient extends EnrollClient<ISession> implements Udfps { private static final String TAG = "FingerprintEnrollClient"; @@ -45,21 +46,22 @@ public class FingerprintEnrollClient extends EnrollClient<ISession> implements U @Nullable private ICancellationSignal mCancellationSignal; private final int mMaxTemplatesPerUser; - public FingerprintEnrollClient(@NonNull Context context, + FingerprintEnrollClient(@NonNull Context context, @NonNull LazyDaemon<ISession> lazyDaemon, @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, int userId, - @NonNull byte[] hardwareAuthToken, @NonNull String owner, @NonNull BiometricUtils utils, - int statsModality, int sensorId, + @NonNull byte[] hardwareAuthToken, @NonNull String owner, + @NonNull BiometricUtils<Fingerprint> utils, int sensorId, @Nullable IUdfpsOverlayController udfpsOvelayController, int maxTemplatesPerUser) { super(context, lazyDaemon, token, listener, userId, hardwareAuthToken, owner, utils, - 0 /* timeoutSec */, statsModality, sensorId, true /* shouldVibrate */); + 0 /* timeoutSec */, BiometricsProtoEnums.MODALITY_FINGERPRINT, sensorId, + true /* shouldVibrate */); mUdfpsOverlayController = udfpsOvelayController; mMaxTemplatesPerUser = maxTemplatesPerUser; } @Override protected boolean hasReachedEnrollmentLimit() { - return FingerprintUtils.getInstance() + return FingerprintUtils.getInstance(getSensorId()) .getBiometricsForUser(getContext(), getTargetUserId()).size() >= mMaxTemplatesPerUser; } @@ -83,7 +85,7 @@ public class FingerprintEnrollClient extends EnrollClient<ISession> implements U protected void startHalOperation() { UdfpsHelper.showUdfpsOverlay(getSensorId(), mUdfpsOverlayController); try { - getFreshDaemon().enroll(mSequentialId, + mCancellationSignal = getFreshDaemon().enroll(mSequentialId, HardwareAuthTokenUtils.toHardwareAuthToken(mHardwareAuthToken)); } catch (RemoteException e) { Slog.e(TAG, "Remote exception when requesting enroll", e); diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGenerateChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGenerateChallengeClient.java index 7db01eed571e..402886b0e92c 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGenerateChallengeClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGenerateChallengeClient.java @@ -19,7 +19,7 @@ package com.android.server.biometrics.sensors.fingerprint.aidl; import android.annotation.NonNull; import android.content.Context; import android.hardware.biometrics.fingerprint.IFingerprint; -import android.hardware.biometrics.fingerprint.IGenerateChallengeCallback; +import android.hardware.biometrics.fingerprint.ISession; import android.os.IBinder; import android.os.RemoteException; import android.util.Slog; @@ -30,28 +30,12 @@ import com.android.server.biometrics.sensors.GenerateChallengeClient; /** * Fingerprint-specific generateChallenge client for the {@link IFingerprint} AIDL HAL interface. */ -public class FingerprintGenerateChallengeClient extends GenerateChallengeClient<IFingerprint> { +class FingerprintGenerateChallengeClient extends GenerateChallengeClient<ISession> { private static final String TAG = "FingerprintGenerateChallengeClient"; private static final int CHALLENGE_TIMEOUT_SEC = 600; // 10 minutes - private final IGenerateChallengeCallback mGenerateChallengeCallback = - new IGenerateChallengeCallback.Stub() { - @Override - public void onChallengeGenerated(int sensorId, int userId, long challenge) { - try { - getListener().onChallengeGenerated(sensorId, challenge); - mCallback.onClientFinished(FingerprintGenerateChallengeClient.this, - true /* success */); - } catch (RemoteException e) { - Slog.e(TAG, "Unable to send challenge", e); - mCallback.onClientFinished(FingerprintGenerateChallengeClient.this, - false /* success */); - } - } - }; - - public FingerprintGenerateChallengeClient(@NonNull Context context, - @NonNull LazyDaemon<IFingerprint> lazyDaemon, + FingerprintGenerateChallengeClient(@NonNull Context context, + @NonNull LazyDaemon<ISession> lazyDaemon, @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, @NonNull String owner, int sensorId) { @@ -61,11 +45,21 @@ public class FingerprintGenerateChallengeClient extends GenerateChallengeClient< @Override protected void startHalOperation() { try { - getFreshDaemon().generateChallenge(getSensorId(), getTargetUserId(), - CHALLENGE_TIMEOUT_SEC, - mGenerateChallengeCallback); + getFreshDaemon().generateChallenge(mSequentialId, CHALLENGE_TIMEOUT_SEC); } catch (RemoteException e) { Slog.e(TAG, "Unable to generateChallenge", e); } } + + void onChallengeGenerated(int sensorId, int userId, long challenge) { + try { + getListener().onChallengeGenerated(sensorId, challenge); + mCallback.onClientFinished(FingerprintGenerateChallengeClient.this, + true /* success */); + } catch (RemoteException e) { + Slog.e(TAG, "Unable to send challenge", e); + mCallback.onClientFinished(FingerprintGenerateChallengeClient.this, + false /* success */); + } + } } diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGetAuthenticatorIdClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGetAuthenticatorIdClient.java new file mode 100644 index 000000000000..fec3cff6d52f --- /dev/null +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGetAuthenticatorIdClient.java @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.biometrics.sensors.fingerprint.aidl; + +import android.annotation.NonNull; +import android.content.Context; +import android.hardware.biometrics.BiometricsProtoEnums; +import android.hardware.biometrics.fingerprint.ISession; +import android.os.RemoteException; +import android.util.Slog; + +import com.android.server.biometrics.sensors.ClientMonitor; + +import java.util.Map; + +class FingerprintGetAuthenticatorIdClient extends ClientMonitor<ISession> { + + private static final String TAG = "FingerprintGetAuthenticatorIdClient"; + + private final Map<Integer, Long> mAuthenticatorIds; + + FingerprintGetAuthenticatorIdClient(@NonNull Context context, + @NonNull LazyDaemon<ISession> lazyDaemon, int userId, @NonNull String owner, + int sensorId, Map<Integer, Long> authenticatorIds) { + super(context, lazyDaemon, null /* token */, null /* listener */, userId, owner, + 0 /* cookie */, sensorId, BiometricsProtoEnums.MODALITY_FINGERPRINT, + BiometricsProtoEnums.ACTION_UNKNOWN, BiometricsProtoEnums.CLIENT_UNKNOWN); + mAuthenticatorIds = authenticatorIds; + } + + @Override + public void unableToStart() { + // Nothing to do here + } + + @Override + protected void startHalOperation() { + try { + getFreshDaemon().getAuthenticatorId(mSequentialId); + } catch (RemoteException e) { + Slog.e(TAG, "Remote exception", e); + } + } + + void onAuthenticatorIdRetrieved(long authenticatorId) { + mAuthenticatorIds.put(getTargetUserId(), authenticatorId); + mCallback.onClientFinished(this, true /* success */); + } +} diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalCleanupClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalCleanupClient.java new file mode 100644 index 000000000000..2a0e984e5933 --- /dev/null +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalCleanupClient.java @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.biometrics.sensors.fingerprint.aidl; + +import android.annotation.NonNull; +import android.content.Context; +import android.hardware.biometrics.BiometricsProtoEnums; +import android.hardware.biometrics.fingerprint.ISession; +import android.hardware.fingerprint.Fingerprint; +import android.os.IBinder; + +import com.android.server.biometrics.sensors.BiometricUtils; +import com.android.server.biometrics.sensors.InternalCleanupClient; +import com.android.server.biometrics.sensors.InternalEnumerateClient; +import com.android.server.biometrics.sensors.RemovalClient; +import com.android.server.biometrics.sensors.fingerprint.FingerprintUtils; + +import java.util.List; +import java.util.Map; + +/** + * Fingerprint-specific internal cleanup client supporting the + * {@link android.hardware.biometrics.fingerprint.IFingerprint} AIDL interface. + */ +class FingerprintInternalCleanupClient extends InternalCleanupClient<Fingerprint, ISession> { + + FingerprintInternalCleanupClient(@NonNull Context context, + @NonNull LazyDaemon<ISession> lazyDaemon, int userId, @NonNull String owner, + int sensorId, @NonNull List<Fingerprint> enrolledList, + @NonNull FingerprintUtils utils, @NonNull Map<Integer, Long> authenticatorIds) { + super(context, lazyDaemon, userId, owner, sensorId, + BiometricsProtoEnums.MODALITY_FINGERPRINT, enrolledList, utils, authenticatorIds); + } + + @Override + protected InternalEnumerateClient<ISession> getEnumerateClient(Context context, + LazyDaemon<ISession> lazyDaemon, IBinder token, int userId, String owner, + List<Fingerprint> enrolledList, BiometricUtils<Fingerprint> utils, int sensorId) { + return new FingerprintInternalEnumerateClient(context, lazyDaemon, token, userId, owner, + enrolledList, utils, sensorId); + } + + @Override + protected RemovalClient<Fingerprint, ISession> getRemovalClient(Context context, + LazyDaemon<ISession> lazyDaemon, IBinder token, int biometricId, int userId, + String owner, BiometricUtils<Fingerprint> utils, int sensorId, + Map<Integer, Long> authenticatorIds) { + return new FingerprintRemovalClient(context, lazyDaemon, token, + null /* ClientMonitorCallbackConverter */, biometricId, userId, owner, utils, + sensorId, authenticatorIds); + } +} diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalEnumerateClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalEnumerateClient.java new file mode 100644 index 000000000000..c9303609c8dc --- /dev/null +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalEnumerateClient.java @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.biometrics.sensors.fingerprint.aidl; + +import android.annotation.NonNull; +import android.content.Context; +import android.hardware.biometrics.BiometricsProtoEnums; +import android.hardware.biometrics.fingerprint.ISession; +import android.hardware.fingerprint.Fingerprint; +import android.os.IBinder; +import android.os.RemoteException; +import android.util.Slog; + +import com.android.server.biometrics.sensors.BiometricUtils; +import com.android.server.biometrics.sensors.InternalEnumerateClient; + +import java.util.List; + +/** + * Fingerprint-specific internal client supporting the + * {@link android.hardware.biometrics.fingerprint.IFingerprint} AIDL interface. + */ +class FingerprintInternalEnumerateClient extends InternalEnumerateClient<ISession> { + private static final String TAG = "FingerprintInternalEnumerateClient"; + + protected FingerprintInternalEnumerateClient(@NonNull Context context, + @NonNull LazyDaemon<ISession> lazyDaemon, @NonNull IBinder token, int userId, + @NonNull String owner, @NonNull List<Fingerprint> enrolledList, + @NonNull BiometricUtils<Fingerprint> utils, int sensorId) { + super(context, lazyDaemon, token, userId, owner, enrolledList, utils, sensorId, + BiometricsProtoEnums.MODALITY_FINGERPRINT); + } + + @Override + protected void startHalOperation() { + try { + getFreshDaemon().enumerateEnrollments(mSequentialId); + } catch (RemoteException e) { + Slog.e(TAG, "Remote exception when requesting enumerate", e); + mCallback.onClientFinished(this, false /* success */); + } + } +} diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java index 0b59086fb907..d713f981b451 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java @@ -19,12 +19,14 @@ package com.android.server.biometrics.sensors.fingerprint.aidl; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityManager; +import android.app.ActivityTaskManager; +import android.app.IActivityTaskManager; +import android.app.TaskStackListener; import android.content.Context; -import android.hardware.biometrics.BiometricsProtoEnums; +import android.content.pm.UserInfo; import android.hardware.biometrics.fingerprint.IFingerprint; import android.hardware.biometrics.fingerprint.SensorProps; import android.hardware.fingerprint.Fingerprint; -import android.hardware.fingerprint.FingerprintManager; import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; import android.hardware.fingerprint.IFingerprintServiceReceiver; import android.hardware.fingerprint.IUdfpsOverlayController; @@ -33,16 +35,21 @@ import android.os.IBinder; import android.os.Looper; import android.os.RemoteException; import android.os.ServiceManager; +import android.os.UserManager; import android.util.Slog; import android.util.SparseArray; import android.view.Surface; +import com.android.server.biometrics.Utils; +import com.android.server.biometrics.sensors.AuthenticationClient; import com.android.server.biometrics.sensors.ClientMonitor; import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter; import com.android.server.biometrics.sensors.LockoutResetDispatcher; +import com.android.server.biometrics.sensors.PerformanceTracker; import com.android.server.biometrics.sensors.fingerprint.FingerprintUtils; import com.android.server.biometrics.sensors.fingerprint.GestureAvailabilityDispatcher; import com.android.server.biometrics.sensors.fingerprint.ServiceProvider; +import com.android.server.biometrics.sensors.fingerprint.Udfps; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -52,6 +59,7 @@ import java.util.List; /** * Provider for a single instance of the {@link IFingerprint} HAL. */ +@SuppressWarnings("deprecation") public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvider { @NonNull private final Context mContext; @@ -60,9 +68,49 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi @NonNull private final ClientMonitor.LazyDaemon<IFingerprint> mLazyDaemon; @NonNull private final Handler mHandler; @NonNull private final LockoutResetDispatcher mLockoutResetDispatcher; + @NonNull private final IActivityTaskManager mActivityTaskManager; + @NonNull private final BiometricTaskStackListener mTaskStackListener; + @Nullable private IFingerprint mDaemon; @Nullable private IUdfpsOverlayController mUdfpsOverlayController; + private final class BiometricTaskStackListener extends TaskStackListener { + @Override + public void onTaskStackChanged() { + mHandler.post(() -> { + for (int i = 0; i < mSensors.size(); i++) { + final ClientMonitor<?> client = mSensors.get(i).getScheduler() + .getCurrentClient(); + if (!(client instanceof AuthenticationClient)) { + Slog.e(getTag(), "Task stack changed for client: " + client); + continue; + } + if (Utils.isKeyguard(mContext, client.getOwnerString())) { + continue; // Keyguard is always allowed + } + + try { + final List<ActivityManager.RunningTaskInfo> runningTasks = + mActivityTaskManager.getTasks(1); + if (!runningTasks.isEmpty()) { + final String topPackage = + runningTasks.get(0).topActivity.getPackageName(); + if (!topPackage.contentEquals(client.getOwnerString()) + && !client.isAlreadyDone()) { + Slog.e(getTag(), "Stopping background authentication, top: " + + topPackage + " currentClient: " + client); + mSensors.get(i).getScheduler() + .cancelAuthentication(client.getToken()); + } + } + } catch (RemoteException e) { + Slog.e(getTag(), "Unable to get running tasks", e); + } + } + }); + } + } + public FingerprintProvider(@NonNull Context context, @NonNull SensorProps[] props, @NonNull String halInstanceName, @NonNull LockoutResetDispatcher lockoutResetDispatcher, @NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher) { @@ -72,6 +120,8 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi mLazyDaemon = this::getHalInstance; mHandler = new Handler(Looper.getMainLooper()); mLockoutResetDispatcher = lockoutResetDispatcher; + mActivityTaskManager = ActivityTaskManager.getService(); + mTaskStackListener = new BiometricTaskStackListener(); for (SensorProps prop : props) { final int sensorId = prop.commonProps.sensorId; @@ -96,15 +146,21 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi @Nullable private synchronized IFingerprint getHalInstance() { - final IFingerprint daemon = IFingerprint.Stub.asInterface( + if (mDaemon != null) { + return mDaemon; + } + + Slog.d(getTag(), "Daemon was null, reconnecting"); + + mDaemon = IFingerprint.Stub.asInterface( ServiceManager.waitForDeclaredService(mHalInstanceName)); - if (daemon == null) { + if (mDaemon == null) { Slog.e(getTag(), "Unable to get daemon"); return null; } try { - daemon.asBinder().linkToDeath(this, 0 /* flags */); + mDaemon.asBinder().linkToDeath(this, 0 /* flags */); } catch (RemoteException e) { Slog.e(getTag(), "Unable to linkToDeath", e); } @@ -115,7 +171,7 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi scheduleInternalCleanup(sensorId, ActivityManager.getCurrentUser()); } - return daemon; + return mDaemon; } private void scheduleForSensor(int sensorId, @NonNull ClientMonitor<?> client) { @@ -135,7 +191,7 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi mSensors.get(sensorId).getScheduler().scheduleClientMonitor(client, callback); } - private void scheduleCreateSessionWithoutHandler(@NonNull IFingerprint daemon, int sensorId, + private void createNewSessionWithoutHandler(@NonNull IFingerprint daemon, int sensorId, int userId) throws RemoteException { // Note that per IFingerprint createSession contract, this method will block until all // existing operations are canceled/finished. However, also note that this is fine, since @@ -144,14 +200,6 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi mSensors.get(sensorId).createNewSession(daemon, sensorId, userId); } - private void scheduleLoadAuthenticatorIdsWithoutHandler(int sensorId) { - - } - - private void scheduleLoadAuthenticatorIds(int sensorId) { - - } - @Override public boolean containsSensor(int sensorId) { return mSensors.contains(sensorId); @@ -167,6 +215,39 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi return props; } + private void scheduleLoadAuthenticatorIds(int sensorId) { + for (UserInfo user : UserManager.get(mContext).getAliveUsers()) { + scheduleLoadAuthenticatorIdsForUser(sensorId, user.id); + } + } + + private void scheduleLoadAuthenticatorIdsForUser(int sensorId, int userId) { + mHandler.post(() -> { + final IFingerprint daemon = getHalInstance(); + if (daemon == null) { + Slog.e(getTag(), "Null daemon during loadAuthenticatorIds, sensorId: " + sensorId); + return; + } + + try { + if (!mSensors.get(sensorId).hasSessionForUser(userId)) { + createNewSessionWithoutHandler(daemon, sensorId, userId); + } + + final FingerprintGetAuthenticatorIdClient client = + new FingerprintGetAuthenticatorIdClient(mContext, + mSensors.get(sensorId).getLazySession(), userId, + mContext.getOpPackageName(), sensorId, + mSensors.get(sensorId).getAuthenticatorIds()); + mSensors.get(sensorId).getScheduler().scheduleClientMonitor(client); + } catch (RemoteException e) { + Slog.e(getTag(), "Remote exception when scheduling loadAuthenticatorId" + + ", sensorId: " + sensorId + + ", userId: " + userId, e); + } + }); + } + @Override public void scheduleResetLockout(int sensorId, int userId, @Nullable byte[] hardwareAuthToken) { mHandler.post(() -> { @@ -178,7 +259,7 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi try { if (!mSensors.get(sensorId).hasSessionForUser(userId)) { - scheduleCreateSessionWithoutHandler(daemon, sensorId, userId); + createNewSessionWithoutHandler(daemon, sensorId, userId); } final FingerprintResetLockoutClient client = new FingerprintResetLockoutClient( @@ -193,24 +274,55 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi } @Override - public void scheduleGenerateChallenge(int sensorId, @NonNull IBinder token, + public void scheduleGenerateChallenge(int sensorId, int userId, @NonNull IBinder token, @NonNull IFingerprintServiceReceiver receiver, String opPackageName) { mHandler.post(() -> { - final FingerprintGenerateChallengeClient client = - new FingerprintGenerateChallengeClient(mContext, mLazyDaemon, token, - new ClientMonitorCallbackConverter(receiver), opPackageName, sensorId); - scheduleForSensor(sensorId, client); + final IFingerprint daemon = getHalInstance(); + if (daemon == null) { + Slog.e(getTag(), "Null daemon during generateChallenge, sensorId: " + sensorId); + return; + } + + try { + if (!mSensors.get(sensorId).hasSessionForUser(userId)) { + createNewSessionWithoutHandler(daemon, sensorId, userId); + } + + final FingerprintGenerateChallengeClient client = + new FingerprintGenerateChallengeClient(mContext, + mSensors.get(sensorId).getLazySession(), token, + new ClientMonitorCallbackConverter(receiver), opPackageName, + sensorId); + scheduleForSensor(sensorId, client); + } catch (RemoteException e) { + Slog.e(getTag(), "Remote exception when scheduling generateChallenge", e); + } }); } @Override - public void scheduleRevokeChallenge(int sensorId, @NonNull IBinder token, + public void scheduleRevokeChallenge(int sensorId, int userId, @NonNull IBinder token, @NonNull String opPackageName, long challenge) { mHandler.post(() -> { - final FingerprintRevokeChallengeClient client = - new FingerprintRevokeChallengeClient(mContext, mLazyDaemon, token, - opPackageName, sensorId, challenge); - scheduleForSensor(sensorId, client); + final IFingerprint daemon = getHalInstance(); + if (daemon == null) { + Slog.e(getTag(), "Null daemon during revokeChallenge, sensorId: " + sensorId); + return; + } + + try { + if (!mSensors.get(sensorId).hasSessionForUser(userId)) { + createNewSessionWithoutHandler(daemon, sensorId, userId); + } + + final FingerprintRevokeChallengeClient client = + new FingerprintRevokeChallengeClient(mContext, + mSensors.get(sensorId).getLazySession(), token, + opPackageName, sensorId, challenge); + scheduleForSensor(sensorId, client); + } catch (RemoteException e) { + Slog.e(getTag(), "Remote exception when scheduling revokeChallenge", e); + } }); } @@ -222,19 +334,15 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi final IFingerprint daemon = getHalInstance(); if (daemon == null) { Slog.e(getTag(), "Null daemon during enroll, sensorId: " + sensorId); - - try { - receiver.onError(FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE, - 0 /* vendorCode */); - } catch (RemoteException e) { - Slog.e(getTag(), "Unable to send HW_UNAVAILABLE", e); - } + // If this happens, we need to send HW_UNAVAILABLE after the scheduler gets to + // this operation. We should not send the callback yet, since the scheduler may + // be processing something else. return; } try { if (!mSensors.get(sensorId).hasSessionForUser(userId)) { - scheduleCreateSessionWithoutHandler(daemon, sensorId, userId); + createNewSessionWithoutHandler(daemon, sensorId, userId); } final int maxTemplatesPerUser = mSensors.get(sensorId).getSensorProperties() @@ -242,15 +350,14 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi final FingerprintEnrollClient client = new FingerprintEnrollClient(mContext, mSensors.get(sensorId).getLazySession(), token, new ClientMonitorCallbackConverter(receiver), userId, hardwareAuthToken, - opPackageName, FingerprintUtils.getInstance(), - BiometricsProtoEnums.MODALITY_FINGERPRINT, sensorId, + opPackageName, FingerprintUtils.getInstance(sensorId), sensorId, mUdfpsOverlayController, maxTemplatesPerUser); scheduleForSensor(sensorId, client, new ClientMonitor.Callback() { @Override public void onClientFinished(@NonNull ClientMonitor<?> clientMonitor, boolean success) { if (success) { - scheduleLoadAuthenticatorIdsWithoutHandler(sensorId); + scheduleLoadAuthenticatorIdsForUser(sensorId, userId); } } }); @@ -262,14 +369,38 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi @Override public void cancelEnrollment(int sensorId, @NonNull IBinder token) { - + mHandler.post(() -> mSensors.get(sensorId).getScheduler().cancelEnrollment(token)); } @Override public void scheduleFingerDetect(int sensorId, @NonNull IBinder token, int userId, @NonNull ClientMonitorCallbackConverter callback, @NonNull String opPackageName, @Nullable Surface surface, int statsClient) { + mHandler.post(() -> { + final IFingerprint daemon = getHalInstance(); + if (daemon == null) { + Slog.e(getTag(), "Null daemon during finger detect, sensorId: " + sensorId); + // If this happens, we need to send HW_UNAVAILABLE after the scheduler gets to + // this operation. We should not send the callback yet, since the scheduler may + // be processing something else. + return; + } + try { + if (!mSensors.get(sensorId).hasSessionForUser(userId)) { + createNewSessionWithoutHandler(daemon, sensorId, userId); + } + + final boolean isStrongBiometric = Utils.isStrongBiometric(sensorId); + final FingerprintDetectClient client = new FingerprintDetectClient(mContext, + mSensors.get(sensorId).getLazySession(), token, callback, userId, + opPackageName, sensorId, mUdfpsOverlayController, isStrongBiometric, + statsClient); + mSensors.get(sensorId).getScheduler().scheduleClientMonitor(client); + } catch (RemoteException e) { + Slog.e(getTag(), "Remote exception when scheduling finger detect", e); + } + }); } @Override @@ -277,65 +408,151 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi int userId, int cookie, @NonNull ClientMonitorCallbackConverter callback, @NonNull String opPackageName, boolean restricted, int statsClient, boolean isKeyguard) { + mHandler.post(() -> { + final IFingerprint daemon = getHalInstance(); + if (daemon == null) { + Slog.e(getTag(), "Null daemon during authenticate, sensorId: " + sensorId); + // If this happens, we need to send HW_UNAVAILABLE after the scheduler gets to + // this operation. We should not send the callback yet, since the scheduler may + // be processing something else. + return; + } + + try { + if (!mSensors.get(sensorId).hasSessionForUser(userId)) { + createNewSessionWithoutHandler(daemon, sensorId, userId); + } + final boolean isStrongBiometric = Utils.isStrongBiometric(sensorId); + final FingerprintAuthenticationClient client = new FingerprintAuthenticationClient( + mContext, mSensors.get(sensorId).getLazySession(), token, callback, userId, + operationId, restricted, opPackageName, cookie, + false /* requireConfirmation */, sensorId, isStrongBiometric, statsClient, + mTaskStackListener, mSensors.get(sensorId).getLockoutCache(), + mUdfpsOverlayController); + mSensors.get(sensorId).getScheduler().scheduleClientMonitor(client); + } catch (RemoteException e) { + Slog.e(getTag(), "Remote exception when scheduling authenticate", e); + } + }); } @Override public void startPreparedClient(int sensorId, int cookie) { - + mHandler.post(() -> mSensors.get(sensorId).getScheduler().startPreparedClient(cookie)); } @Override public void cancelAuthentication(int sensorId, @NonNull IBinder token) { - + mHandler.post(() -> mSensors.get(sensorId).getScheduler().cancelAuthentication(token)); } @Override public void scheduleRemove(int sensorId, @NonNull IBinder token, @NonNull IFingerprintServiceReceiver receiver, int fingerId, int userId, @NonNull String opPackageName) { + mHandler.post(() -> { + final IFingerprint daemon = getHalInstance(); + if (daemon == null) { + Slog.e(getTag(), "Null daemon during remove, sensorId: " + sensorId); + // If this happens, we need to send HW_UNAVAILABLE after the scheduler gets to + // this operation. We should not send the callback yet, since the scheduler may + // be processing something else. + return; + } + + try { + if (!mSensors.get(sensorId).hasSessionForUser(userId)) { + createNewSessionWithoutHandler(daemon, sensorId, userId); + } + final FingerprintRemovalClient client = new FingerprintRemovalClient(mContext, + mSensors.get(sensorId).getLazySession(), token, + new ClientMonitorCallbackConverter(receiver), fingerId, userId, + opPackageName, FingerprintUtils.getInstance(sensorId), sensorId, + mSensors.get(sensorId).getAuthenticatorIds()); + mSensors.get(sensorId).getScheduler().scheduleClientMonitor(client); + } catch (RemoteException e) { + Slog.e(getTag(), "Remote exception when scheduling remove", e); + } + }); } @Override - public void scheduleInternalCleanup(int userId, int sensorId) { + public void scheduleInternalCleanup(int sensorId, int userId) { + mHandler.post(() -> { + final IFingerprint daemon = getHalInstance(); + if (daemon == null) { + Slog.e(getTag(), "Null daemon during internal cleanup, sensorId: " + sensorId); + return; + } + try { + if (!mSensors.get(sensorId).hasSessionForUser(userId)) { + createNewSessionWithoutHandler(daemon, sensorId, userId); + } + + final List<Fingerprint> enrolledList = getEnrolledFingerprints(sensorId, userId); + final FingerprintInternalCleanupClient client = + new FingerprintInternalCleanupClient(mContext, + mSensors.get(sensorId).getLazySession(), userId, + mContext.getOpPackageName(), sensorId, enrolledList, + FingerprintUtils.getInstance(sensorId), + mSensors.get(sensorId).getAuthenticatorIds()); + mSensors.get(sensorId).getScheduler().scheduleClientMonitor(client); + } catch (RemoteException e) { + Slog.e(getTag(), "Remote exception when scheduling internal cleanup", e); + } + }); } @Override public boolean isHardwareDetected(int sensorId) { - return false; + return getHalInstance() != null; } @Override public void rename(int sensorId, int fingerId, int userId, @NonNull String name) { - + FingerprintUtils.getInstance(sensorId) + .renameBiometricForUser(mContext, userId, fingerId, name); } @NonNull @Override public List<Fingerprint> getEnrolledFingerprints(int sensorId, int userId) { - return new ArrayList<>(); + return FingerprintUtils.getInstance(sensorId).getBiometricsForUser(mContext, userId); } @Override public int getLockoutModeForUser(int sensorId, int userId) { - return 0; + return mSensors.get(sensorId).getLockoutCache().getLockoutModeForUser(userId); } @Override public long getAuthenticatorId(int sensorId, int userId) { - return 0; + return mSensors.get(sensorId).getAuthenticatorIds().getOrDefault(userId, 0L); } @Override public void onPointerDown(int sensorId, int x, int y, float minor, float major) { - + final ClientMonitor<?> client = mSensors.get(sensorId).getScheduler().getCurrentClient(); + if (!(client instanceof Udfps)) { + Slog.e(getTag(), "onPointerDown received during client: " + client); + return; + } + final Udfps udfps = (Udfps) client; + udfps.onPointerDown(x, y, minor, major); } @Override public void onPointerUp(int sensorId) { - + final ClientMonitor<?> client = mSensors.get(sensorId).getScheduler().getCurrentClient(); + if (!(client instanceof Udfps)) { + Slog.e(getTag(), "onPointerUp received during client: " + client); + return; + } + final Udfps udfps = (Udfps) client; + udfps.onPointerUp(); } @Override @@ -355,6 +572,14 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi @Override public void binderDied() { + Slog.e(getTag(), "HAL died"); + mHandler.post(() -> { + mDaemon = null; + for (int i = 0; i < mSensors.size(); i++) { + final int sensorId = mSensors.keyAt(i); + PerformanceTracker.getInstanceForSensorId(sensorId).incrementHALDeathCount(); + } + }); } } diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintRemovalClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintRemovalClient.java new file mode 100644 index 000000000000..4a99a7b29638 --- /dev/null +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintRemovalClient.java @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.biometrics.sensors.fingerprint.aidl; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.Context; +import android.hardware.biometrics.BiometricsProtoEnums; +import android.hardware.biometrics.fingerprint.ISession; +import android.hardware.fingerprint.Fingerprint; +import android.os.IBinder; +import android.os.RemoteException; +import android.util.Slog; + +import com.android.server.biometrics.sensors.BiometricUtils; +import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter; +import com.android.server.biometrics.sensors.RemovalClient; + +import java.util.Map; + +/** + * Fingerprint-specific removal client supporting the + * {@link android.hardware.biometrics.fingerprint.IFingerprint} interface. + */ +class FingerprintRemovalClient extends RemovalClient<Fingerprint, ISession> { + private static final String TAG = "FingerprintRemovalClient"; + + FingerprintRemovalClient(@NonNull Context context, + @NonNull LazyDaemon<ISession> lazyDaemon, @NonNull IBinder token, + @Nullable ClientMonitorCallbackConverter listener, int biometricId, int userId, + @NonNull String owner, @NonNull BiometricUtils<Fingerprint> utils, int sensorId, + @NonNull Map<Integer, Long> authenticatorIds) { + super(context, lazyDaemon, token, listener, biometricId, userId, owner, utils, sensorId, + authenticatorIds, BiometricsProtoEnums.MODALITY_FINGERPRINT); + } + + @Override + protected void startHalOperation() { + try { + final int[] ids = new int[] {mBiometricId}; + getFreshDaemon().removeEnrollments(mSequentialId, ids); + } catch (RemoteException e) { + Slog.e(TAG, "Remote exception when requesting remove", e); + mCallback.onClientFinished(this, false /* success */); + } + } +} diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintResetLockoutClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintResetLockoutClient.java index bc35ad4b2807..17181264c620 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintResetLockoutClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintResetLockoutClient.java @@ -35,7 +35,7 @@ import com.android.server.biometrics.sensors.LockoutTracker; * Updates the framework's lockout cache and notifies clients such as Keyguard when lockout is * cleared. */ -public class FingerprintResetLockoutClient extends ClientMonitor<ISession> { +class FingerprintResetLockoutClient extends ClientMonitor<ISession> { private static final String TAG = "FingerprintResetLockoutClient"; @@ -43,7 +43,7 @@ public class FingerprintResetLockoutClient extends ClientMonitor<ISession> { private final LockoutCache mLockoutCache; private final LockoutResetDispatcher mLockoutResetDispatcher; - public FingerprintResetLockoutClient(@NonNull Context context, + FingerprintResetLockoutClient(@NonNull Context context, @NonNull LazyDaemon<ISession> lazyDaemon, int userId, String owner, int sensorId, @NonNull byte[] hardwareAuthToken, @NonNull LockoutCache lockoutTracker, @NonNull LockoutResetDispatcher lockoutResetDispatcher) { diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintRevokeChallengeClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintRevokeChallengeClient.java index e97dbe795f74..ebb4fe63e4a1 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintRevokeChallengeClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintRevokeChallengeClient.java @@ -19,7 +19,7 @@ package com.android.server.biometrics.sensors.fingerprint.aidl; import android.annotation.NonNull; import android.content.Context; import android.hardware.biometrics.fingerprint.IFingerprint; -import android.hardware.biometrics.fingerprint.IRevokeChallengeCallback; +import android.hardware.biometrics.fingerprint.ISession; import android.os.IBinder; import android.os.RemoteException; import android.util.Slog; @@ -29,25 +29,14 @@ import com.android.server.biometrics.sensors.RevokeChallengeClient; /** * Fingerprint-specific revokeChallenge client for the {@link IFingerprint} AIDL HAL interface. */ -public class FingerprintRevokeChallengeClient extends RevokeChallengeClient<IFingerprint> { +class FingerprintRevokeChallengeClient extends RevokeChallengeClient<ISession> { private static final String TAG = "FingerpirntRevokeChallengeClient"; private final long mChallenge; - private final IRevokeChallengeCallback mRevokeChallengeCallback = - new IRevokeChallengeCallback.Stub() { - @Override - public void onChallengeRevoked(int sensorId, int userId, long challenge) { - final boolean success = challenge == mChallenge; - mCallback.onClientFinished(FingerprintRevokeChallengeClient.this, success); - } - }; - - public FingerprintRevokeChallengeClient( - @NonNull Context context, - @NonNull LazyDaemon<IFingerprint> lazyDaemon, - @NonNull IBinder token, + FingerprintRevokeChallengeClient(@NonNull Context context, + @NonNull LazyDaemon<ISession> lazyDaemon, @NonNull IBinder token, @NonNull String owner, int sensorId, long challenge) { super(context, lazyDaemon, token, owner, sensorId); mChallenge = challenge; @@ -56,10 +45,14 @@ public class FingerprintRevokeChallengeClient extends RevokeChallengeClient<IFin @Override protected void startHalOperation() { try { - getFreshDaemon().revokeChallenge(getSensorId(), getTargetUserId(), mChallenge, - mRevokeChallengeCallback); + getFreshDaemon().revokeChallenge(mSequentialId, mChallenge); } catch (RemoteException e) { Slog.e(TAG, "Unable to revokeChallenge", e); } } + + void onChallengeRevoked(int sensorId, int userId, long challenge) { + final boolean success = challenge == mChallenge; + mCallback.onClientFinished(FingerprintRevokeChallengeClient.this, success); + } } diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/LockoutCache.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/LockoutCache.java index 2abbcb0c7493..2fae1f3d9d43 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/LockoutCache.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/LockoutCache.java @@ -23,7 +23,7 @@ import com.android.server.biometrics.sensors.LockoutTracker; /** * For a single sensor, caches lockout states for all users. */ -public class LockoutCache implements LockoutTracker { +class LockoutCache implements LockoutTracker { // Map of userId to LockoutMode private final SparseIntArray mUserLockoutStates; diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java index 2ed695d8821e..d4ce896d42ec 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java @@ -19,46 +19,78 @@ package com.android.server.biometrics.sensors.fingerprint.aidl; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; +import android.hardware.biometrics.BiometricsProtoEnums; import android.hardware.biometrics.fingerprint.Error; import android.hardware.biometrics.fingerprint.IFingerprint; import android.hardware.biometrics.fingerprint.ISession; import android.hardware.biometrics.fingerprint.ISessionCallback; import android.hardware.fingerprint.Fingerprint; +import android.hardware.fingerprint.FingerprintManager; import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; import android.hardware.keymaster.HardwareAuthToken; import android.os.Handler; +import android.os.IBinder; import android.os.RemoteException; import android.util.Slog; +import com.android.internal.util.FrameworkStatsLog; import com.android.server.biometrics.HardwareAuthTokenUtils; import com.android.server.biometrics.Utils; import com.android.server.biometrics.sensors.AcquisitionClient; import com.android.server.biometrics.sensors.AuthenticationConsumer; import com.android.server.biometrics.sensors.BiometricScheduler; import com.android.server.biometrics.sensors.ClientMonitor; +import com.android.server.biometrics.sensors.EnumerateConsumer; import com.android.server.biometrics.sensors.Interruptable; +import com.android.server.biometrics.sensors.LockoutConsumer; +import com.android.server.biometrics.sensors.RemovalConsumer; import com.android.server.biometrics.sensors.fingerprint.FingerprintUtils; import com.android.server.biometrics.sensors.fingerprint.GestureAvailabilityDispatcher; import java.util.ArrayList; import java.util.Arrays; +import java.util.HashMap; import java.util.List; +import java.util.Map; /** * Maintains the state of a single sensor within an instance of the * {@link android.hardware.biometrics.fingerprint.IFingerprint} HAL. */ -class Sensor { +@SuppressWarnings("deprecation") +class Sensor implements IBinder.DeathRecipient { @NonNull private final String mTag; @NonNull private final Context mContext; @NonNull private final Handler mHandler; @NonNull private final FingerprintSensorPropertiesInternal mSensorProperties; @NonNull private final BiometricScheduler mScheduler; @NonNull private final LockoutCache mLockoutCache; + @NonNull private final Map<Integer, Long> mAuthenticatorIds; - @Nullable private Session mCurrentSession; // TODO: Death recipient + @Nullable private Session mCurrentSession; @NonNull private final ClientMonitor.LazyDaemon<ISession> mLazySession; + @Override + public void binderDied() { + Slog.e(mTag, "Binder died"); + mHandler.post(() -> { + final ClientMonitor<?> client = mScheduler.getCurrentClient(); + if (client instanceof Interruptable) { + Slog.e(mTag, "Sending ERROR_HW_UNAVAILABLE for client: " + client); + final Interruptable interruptable = (Interruptable) client; + interruptable.onError(FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE, + 0 /* vendorCode */); + + mScheduler.recordCrashState(); + + FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED, + BiometricsProtoEnums.MODALITY_FINGERPRINT, + BiometricsProtoEnums.ISSUE_HAL_DEATH); + mCurrentSession = null; + } + }); + } + private static class Session { @NonNull private final String mTag; @NonNull private final ISession mSession; @@ -84,6 +116,7 @@ class Sensor { mSensorProperties = sensorProperties; mScheduler = new BiometricScheduler(tag, gestureAvailabilityDispatcher); mLockoutCache = new LockoutCache(); + mAuthenticatorIds = new HashMap<>(); mLazySession = () -> mCurrentSession != null ? mCurrentSession.mSession : null; } @@ -95,6 +128,7 @@ class Sensor { return mSensorProperties; } + @SuppressWarnings("BooleanMethodIsAlwaysInverted") boolean hasSessionForUser(int userId) { return mCurrentSession != null && mCurrentSession.mUserId == userId; } @@ -104,7 +138,39 @@ class Sensor { final ISessionCallback callback = new ISessionCallback.Stub() { @Override public void onStateChanged(int cookie, byte state) { + // TODO(b/162973174) + } + + @Override + public void onChallengeGenerated(long challenge) { + mHandler.post(() -> { + final ClientMonitor<?> client = mScheduler.getCurrentClient(); + if (!(client instanceof FingerprintGenerateChallengeClient)) { + Slog.e(mTag, "onChallengeGenerated for wrong client: " + + Utils.getClientName(client)); + return; + } + + final FingerprintGenerateChallengeClient generateChallengeClient = + (FingerprintGenerateChallengeClient) client; + generateChallengeClient.onChallengeGenerated(sensorId, userId, challenge); + }); + } + + @Override + public void onChallengeRevoked(long challenge) { + mHandler.post(() -> { + final ClientMonitor<?> client = mScheduler.getCurrentClient(); + if (!(client instanceof FingerprintRevokeChallengeClient)) { + Slog.e(mTag, "onChallengeRevoked for wrong client: " + + Utils.getClientName(client)); + return; + } + final FingerprintRevokeChallengeClient revokeChallengeClient = + (FingerprintRevokeChallengeClient) client; + revokeChallengeClient.onChallengeRevoked(sensorId, userId, challenge); + }); } @Override @@ -157,7 +223,7 @@ class Sensor { } final int currentUserId = client.getTargetUserId(); - final CharSequence name = FingerprintUtils.getInstance() + final CharSequence name = FingerprintUtils.getInstance(sensorId) .getUniqueName(mContext, currentUserId); final Fingerprint fingerprint = new Fingerprint(name, enrollmentId, sensorId); @@ -209,12 +275,32 @@ class Sensor { @Override public void onLockoutTimed(long durationMillis) { + mHandler.post(() -> { + final ClientMonitor<?> client = mScheduler.getCurrentClient(); + if (!(client instanceof LockoutConsumer)) { + Slog.e(mTag, "onLockoutTimed for non-lockout consumer: " + + Utils.getClientName(client)); + return; + } + final LockoutConsumer lockoutConsumer = (LockoutConsumer) client; + lockoutConsumer.onLockoutTimed(durationMillis); + }); } @Override public void onLockoutPermanent() { + mHandler.post(() -> { + final ClientMonitor<?> client = mScheduler.getCurrentClient(); + if (!(client instanceof LockoutConsumer)) { + Slog.e(mTag, "onLockoutPermanent for non-lockout consumer: " + + Utils.getClientName(client)); + return; + } + final LockoutConsumer lockoutConsumer = (LockoutConsumer) client; + lockoutConsumer.onLockoutPermanent(); + }); } @Override @@ -235,31 +321,89 @@ class Sensor { @Override public void onInteractionDetected() { + mHandler.post(() -> { + final ClientMonitor<?> client = mScheduler.getCurrentClient(); + if (!(client instanceof FingerprintDetectClient)) { + Slog.e(mTag, "onInteractionDetected for non-detect client: " + + Utils.getClientName(client)); + return; + } + final FingerprintDetectClient fingerprintDetectClient = + (FingerprintDetectClient) client; + fingerprintDetectClient.onInteractionDetected(); + }); } @Override public void onEnrollmentsEnumerated(int[] enrollmentIds) { + mHandler.post(() -> { + final ClientMonitor<?> client = mScheduler.getCurrentClient(); + if (!(client instanceof EnumerateConsumer)) { + Slog.e(mTag, "onEnrollmentsEnumerated for non-enumerate consumer: " + + Utils.getClientName(client)); + return; + } + final EnumerateConsumer enumerateConsumer = + (EnumerateConsumer) client; + if (enrollmentIds.length > 0) { + for (int i = 0; i < enrollmentIds.length; i++) { + final Fingerprint fp = new Fingerprint("", enrollmentIds[i], sensorId); + enumerateConsumer.onEnumerationResult(fp, enrollmentIds.length - i - 1); + } + } else { + enumerateConsumer.onEnumerationResult(null /* identifier */, 0); + } + }); } @Override public void onEnrollmentsRemoved(int[] enrollmentIds) { + mHandler.post(() -> { + final ClientMonitor<?> client = mScheduler.getCurrentClient(); + if (!(client instanceof RemovalConsumer)) { + Slog.e(mTag, "onRemoved for non-removal consumer: " + + Utils.getClientName(client)); + return; + } + final RemovalConsumer removalConsumer = (RemovalConsumer) client; + if (enrollmentIds.length > 0) { + for (int i = 0; i < enrollmentIds.length; i++) { + final Fingerprint fp = new Fingerprint("", enrollmentIds[i], sensorId); + removalConsumer.onRemoved(fp, enrollmentIds.length - i - 1); + } + } else { + removalConsumer.onRemoved(null, 0); + } + }); } @Override public void onAuthenticatorIdRetrieved(long authenticatorId) { + mHandler.post(() -> { + final ClientMonitor<?> client = mScheduler.getCurrentClient(); + if (!(client instanceof FingerprintGetAuthenticatorIdClient)) { + Slog.e(mTag, "onAuthenticatorIdRetrieved for wrong consumer: " + + Utils.getClientName(client)); + return; + } + final FingerprintGetAuthenticatorIdClient getAuthenticatorIdClient = + (FingerprintGetAuthenticatorIdClient) client; + getAuthenticatorIdClient.onAuthenticatorIdRetrieved(authenticatorId); + }); } @Override public void onAuthenticatorIdInvalidated() { - + // TODO(159667191) } }; final ISession newSession = daemon.createSession(sensorId, userId, callback); + newSession.asBinder().linkToDeath(this, 0 /* flags */); mCurrentSession = new Session(mTag, newSession, userId, callback); } @@ -270,4 +414,8 @@ class Sensor { @NonNull LockoutCache getLockoutCache() { return mLockoutCache; } + + @NonNull Map<Integer, Long> getAuthenticatorIds() { + return mAuthenticatorIds; + } } diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java index fedcfa576d35..ab4427c5235c 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java @@ -505,7 +505,7 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider } @Override - public void scheduleGenerateChallenge(int sensorId, @NonNull IBinder token, + public void scheduleGenerateChallenge(int sensorId, int userId, @NonNull IBinder token, @NonNull IFingerprintServiceReceiver receiver, @NonNull String opPackageName) { mHandler.post(() -> { final FingerprintGenerateChallengeClient client = @@ -517,7 +517,7 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider } @Override - public void scheduleRevokeChallenge(int sensorId, @NonNull IBinder token, + public void scheduleRevokeChallenge(int sensorId, int userId, @NonNull IBinder token, @NonNull String opPackageName, long challenge) { mHandler.post(() -> { final FingerprintRevokeChallengeClient client = new FingerprintRevokeChallengeClient( @@ -594,16 +594,12 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider @Override public void startPreparedClient(int sensorId, int cookie) { - mHandler.post(() -> { - mScheduler.startPreparedClient(cookie); - }); + mHandler.post(() -> mScheduler.startPreparedClient(cookie)); } @Override public void cancelAuthentication(int sensorId, @NonNull IBinder token) { - mHandler.post(() -> { - mScheduler.cancelAuthentication(token); - }); + mHandler.post(() -> mScheduler.cancelAuthentication(token)); } @Override @@ -636,7 +632,7 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider } @Override - public void scheduleInternalCleanup(int userId, int sensorId) { + public void scheduleInternalCleanup(int sensorId, int userId) { scheduleInternalCleanup(userId); } diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java index 975ac3d6f2bf..c750b906c9b6 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintEnrollClient.java @@ -22,6 +22,7 @@ import android.content.Context; import android.hardware.biometrics.BiometricFingerprintConstants; import android.hardware.biometrics.BiometricsProtoEnums; import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint; +import android.hardware.fingerprint.Fingerprint; import android.hardware.fingerprint.IUdfpsOverlayController; import android.os.IBinder; import android.os.RemoteException; @@ -48,8 +49,8 @@ public class FingerprintEnrollClient extends EnrollClient<IBiometricsFingerprint FingerprintEnrollClient(@NonNull Context context, @NonNull LazyDaemon<IBiometricsFingerprint> lazyDaemon, @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, int userId, - @NonNull byte[] hardwareAuthToken, @NonNull String owner, @NonNull BiometricUtils utils, - int timeoutSec, int sensorId, + @NonNull byte[] hardwareAuthToken, @NonNull String owner, + @NonNull BiometricUtils<Fingerprint> utils, int timeoutSec, int sensorId, @Nullable IUdfpsOverlayController udfpsOverlayController) { super(context, lazyDaemon, token, listener, userId, hardwareAuthToken, owner, utils, timeoutSec, BiometricsProtoEnums.MODALITY_FINGERPRINT, sensorId, diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintInternalCleanupClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintInternalCleanupClient.java index e0611120c69a..a42a8ae24926 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintInternalCleanupClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintInternalCleanupClient.java @@ -42,7 +42,8 @@ class FingerprintInternalCleanupClient FingerprintInternalCleanupClient(@NonNull Context context, @NonNull LazyDaemon<IBiometricsFingerprint> lazyDaemon, int userId, @NonNull String owner, int sensorId, @NonNull List<Fingerprint> enrolledList, - @NonNull BiometricUtils utils, @NonNull Map<Integer, Long> authenticatorIds) { + @NonNull BiometricUtils<Fingerprint> utils, + @NonNull Map<Integer, Long> authenticatorIds) { super(context, lazyDaemon, userId, owner, sensorId, BiometricsProtoEnums.MODALITY_FINGERPRINT, enrolledList, utils, authenticatorIds); } @@ -50,18 +51,17 @@ class FingerprintInternalCleanupClient @Override protected InternalEnumerateClient<IBiometricsFingerprint> getEnumerateClient( Context context, LazyDaemon<IBiometricsFingerprint> lazyDaemon, IBinder token, - int userId, String owner, - List<Fingerprint> enrolledList, BiometricUtils utils, - int sensorId) { + int userId, String owner, List<Fingerprint> enrolledList, + BiometricUtils<Fingerprint> utils, int sensorId) { return new FingerprintInternalEnumerateClient(context, lazyDaemon, token, userId, owner, enrolledList, utils, sensorId); } @Override - protected RemovalClient<IBiometricsFingerprint> getRemovalClient(Context context, + protected RemovalClient<Fingerprint, IBiometricsFingerprint> getRemovalClient(Context context, LazyDaemon<IBiometricsFingerprint> lazyDaemon, IBinder token, - int biometricId, int userId, String owner, BiometricUtils utils, int sensorId, - Map<Integer, Long> authenticatorIds) { + int biometricId, int userId, String owner, BiometricUtils<Fingerprint> utils, + int sensorId, Map<Integer, Long> authenticatorIds) { // Internal remove does not need to send results to anyone. Cleanup (enumerate + remove) // is all done internally. return new FingerprintRemovalClient(context, lazyDaemon, token, diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintInternalEnumerateClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintInternalEnumerateClient.java index 5fd1d1e812ce..7117cf382a60 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintInternalEnumerateClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintInternalEnumerateClient.java @@ -41,7 +41,7 @@ class FingerprintInternalEnumerateClient extends InternalEnumerateClient<IBiomet FingerprintInternalEnumerateClient(@NonNull Context context, @NonNull LazyDaemon<IBiometricsFingerprint> lazyDaemon, @NonNull IBinder token, int userId, @NonNull String owner, @NonNull List<Fingerprint> enrolledList, - @NonNull BiometricUtils utils, int sensorId) { + @NonNull BiometricUtils<Fingerprint> utils, int sensorId) { super(context, lazyDaemon, token, userId, owner, enrolledList, utils, sensorId, BiometricsProtoEnums.MODALITY_FINGERPRINT); } diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintRemovalClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintRemovalClient.java index 4bbb7efa2166..f6a22f581f1a 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintRemovalClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintRemovalClient.java @@ -20,6 +20,7 @@ import android.annotation.NonNull; import android.content.Context; import android.hardware.biometrics.BiometricsProtoEnums; import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint; +import android.hardware.fingerprint.Fingerprint; import android.os.IBinder; import android.os.RemoteException; import android.util.Slog; @@ -35,13 +36,13 @@ import java.util.Map; * {@link android.hardware.biometrics.fingerprint.V2_1} and * {@link android.hardware.biometrics.fingerprint.V2_2} HIDL interfaces. */ -class FingerprintRemovalClient extends RemovalClient<IBiometricsFingerprint> { +class FingerprintRemovalClient extends RemovalClient<Fingerprint, IBiometricsFingerprint> { private static final String TAG = "FingerprintRemovalClient"; FingerprintRemovalClient(@NonNull Context context, @NonNull LazyDaemon<IBiometricsFingerprint> lazyDaemon, @NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, int biometricId, int userId, - @NonNull String owner, @NonNull BiometricUtils utils, int sensorId, + @NonNull String owner, @NonNull BiometricUtils<Fingerprint> utils, int sensorId, @NonNull Map<Integer, Long> authenticatorIds) { super(context, lazyDaemon, token, listener, biometricId, userId, owner, utils, sensorId, authenticatorIds, BiometricsProtoEnums.MODALITY_FINGERPRINT); diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/LockoutFrameworkImpl.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/LockoutFrameworkImpl.java index 4fc1545c4334..dc5dace98825 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/LockoutFrameworkImpl.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/LockoutFrameworkImpl.java @@ -135,6 +135,6 @@ public class LockoutFrameworkImpl implements LockoutTracker { private PendingIntent getLockoutResetIntentForUser(int userId) { return PendingIntent.getBroadcast(mContext, userId, new Intent(ACTION_LOCKOUT_RESET).putExtra(KEY_LOCKOUT_RESET_USER, userId), - PendingIntent.FLAG_UPDATE_CURRENT); + PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE); } } diff --git a/services/core/java/com/android/server/compat/CompatChange.java b/services/core/java/com/android/server/compat/CompatChange.java index 2e9818d15963..bc3bff1b966f 100644 --- a/services/core/java/com/android/server/compat/CompatChange.java +++ b/services/core/java/com/android/server/compat/CompatChange.java @@ -63,7 +63,7 @@ public final class CompatChange extends CompatibilityChangeInfo { private Map<String, Boolean> mPackageOverrides; public CompatChange(long changeId) { - this(changeId, null, -1, false, false, null); + this(changeId, null, -1, -1, false, false, null); } /** @@ -71,11 +71,14 @@ public final class CompatChange extends CompatibilityChangeInfo { * @param name Short descriptive name. * @param enableAfterTargetSdk {@code targetSdkVersion} restriction. See {@link EnabledAfter}; * -1 if the change is always enabled. + * @param enableSinceTargetSdk {@code targetSdkVersion} restriction. See {@link EnabledSince}; + * -1 if the change is always enabled. * @param disabled If {@code true}, overrides any {@code enableAfterTargetSdk} set. */ public CompatChange(long changeId, @Nullable String name, int enableAfterTargetSdk, - boolean disabled, boolean loggingOnly, String description) { - super(changeId, name, enableAfterTargetSdk, disabled, loggingOnly, description); + int enableSinceTargetSdk, boolean disabled, boolean loggingOnly, String description) { + super(changeId, name, enableAfterTargetSdk, enableSinceTargetSdk, disabled, loggingOnly, + description); } /** @@ -83,7 +86,8 @@ public final class CompatChange extends CompatibilityChangeInfo { */ public CompatChange(Change change) { super(change.getId(), change.getName(), change.getEnableAfterTargetSdk(), - change.getDisabled(), change.getLoggingOnly(), change.getDescription()); + change.getEnableSinceTargetSdk(), change.getDisabled(), change.getLoggingOnly(), + change.getDescription()); } void registerListener(ChangeListener listener) { @@ -145,8 +149,8 @@ public final class CompatChange extends CompatibilityChangeInfo { if (getDisabled()) { return false; } - if (getEnableAfterTargetSdk() != -1) { - return app.targetSdkVersion > getEnableAfterTargetSdk(); + if (getEnableSinceTargetSdk() != -1) { + return app.targetSdkVersion >= getEnableSinceTargetSdk(); } return true; } @@ -167,8 +171,8 @@ public final class CompatChange extends CompatibilityChangeInfo { if (getName() != null) { sb.append("; name=").append(getName()); } - if (getEnableAfterTargetSdk() != -1) { - sb.append("; enableAfterTargetSdk=").append(getEnableAfterTargetSdk()); + if (getEnableSinceTargetSdk() != -1) { + sb.append("; enableSinceTargetSdk=").append(getEnableSinceTargetSdk()); } if (getDisabled()) { sb.append("; disabled"); diff --git a/services/core/java/com/android/server/compat/CompatConfig.java b/services/core/java/com/android/server/compat/CompatConfig.java index aeaa1fedf9e3..d80c58b39768 100644 --- a/services/core/java/com/android/server/compat/CompatConfig.java +++ b/services/core/java/com/android/server/compat/CompatConfig.java @@ -192,16 +192,19 @@ final class CompatConfig { } /** - * Returns the minimum sdk version for which this change should be enabled (or 0 if it is not + * Returns the maximum sdk version for which this change can be opted in (or -1 if it is not * target sdk gated). */ - int minTargetSdkForChangeId(long changeId) { + int maxTargetSdkForChangeIdOptIn(long changeId) { synchronized (mChanges) { CompatChange c = mChanges.get(changeId); if (c == null) { - return 0; + return -1; } - return c.getEnableAfterTargetSdk(); + if (c.getEnableSinceTargetSdk() != -1) { + return c.getEnableSinceTargetSdk() - 1; + } + return -1; } } @@ -318,7 +321,7 @@ final class CompatConfig { } } - private long[] getAllowedChangesAfterTargetSdkForPackage(String packageName, + private long[] getAllowedChangesSinceTargetSdkForPackage(String packageName, int targetSdkVersion) throws RemoteException { LongArray allowed = new LongArray(); @@ -326,7 +329,7 @@ final class CompatConfig { for (int i = 0; i < mChanges.size(); ++i) { try { CompatChange change = mChanges.valueAt(i); - if (change.getEnableAfterTargetSdk() != targetSdkVersion) { + if (change.getEnableSinceTargetSdk() != targetSdkVersion) { continue; } OverrideAllowedState allowedState = @@ -345,14 +348,14 @@ final class CompatConfig { } /** - * Enables all changes with enabledAfterTargetSdk == {@param targetSdkVersion} for + * Enables all changes with enabledSinceTargetSdk == {@param targetSdkVersion} for * {@param packageName}. * * @return The number of changes that were toggled. */ int enableTargetSdkChangesForPackage(String packageName, int targetSdkVersion) throws RemoteException { - long[] changes = getAllowedChangesAfterTargetSdkForPackage(packageName, targetSdkVersion); + long[] changes = getAllowedChangesSinceTargetSdkForPackage(packageName, targetSdkVersion); for (long changeId : changes) { addOverride(changeId, packageName, true); } @@ -361,14 +364,14 @@ final class CompatConfig { /** - * Disables all changes with enabledAfterTargetSdk == {@param targetSdkVersion} for + * Disables all changes with enabledSinceTargetSdk == {@param targetSdkVersion} for * {@param packageName}. * * @return The number of changes that were toggled. */ int disableTargetSdkChangesForPackage(String packageName, int targetSdkVersion) throws RemoteException { - long[] changes = getAllowedChangesAfterTargetSdkForPackage(packageName, targetSdkVersion); + long[] changes = getAllowedChangesSinceTargetSdkForPackage(packageName, targetSdkVersion); for (long changeId : changes) { addOverride(changeId, packageName, false); } @@ -448,12 +451,7 @@ final class CompatConfig { CompatibilityChangeInfo[] changeInfos = new CompatibilityChangeInfo[mChanges.size()]; for (int i = 0; i < mChanges.size(); ++i) { CompatChange change = mChanges.valueAt(i); - changeInfos[i] = new CompatibilityChangeInfo(change.getId(), - change.getName(), - change.getEnableAfterTargetSdk(), - change.getDisabled(), - change.getLoggingOnly(), - change.getDescription()); + changeInfos[i] = new CompatibilityChangeInfo(change); } return changeInfos; } diff --git a/services/core/java/com/android/server/compat/OverrideValidatorImpl.java b/services/core/java/com/android/server/compat/OverrideValidatorImpl.java index 08d266478f4b..79a13ca242c1 100644 --- a/services/core/java/com/android/server/compat/OverrideValidatorImpl.java +++ b/services/core/java/com/android/server/compat/OverrideValidatorImpl.java @@ -58,7 +58,7 @@ public class OverrideValidatorImpl extends IOverrideValidator.Stub { boolean debuggableBuild = mAndroidBuildClassifier.isDebuggableBuild(); boolean finalBuild = mAndroidBuildClassifier.isFinalBuild(); - int minTargetSdk = mCompatConfig.minTargetSdkForChangeId(changeId); + int maxTargetSdk = mCompatConfig.maxTargetSdkForChangeIdOptIn(changeId); boolean disabled = mCompatConfig.isDisabled(changeId); // Allow any override for userdebug or eng builds. @@ -82,16 +82,16 @@ public class OverrideValidatorImpl extends IOverrideValidator.Stub { } // Allow overriding any change for debuggable apps on non-final builds. if (!finalBuild) { - return new OverrideAllowedState(ALLOWED, appTargetSdk, minTargetSdk); + return new OverrideAllowedState(ALLOWED, appTargetSdk, maxTargetSdk); } // Do not allow overriding default enabled changes on user builds - if (minTargetSdk == -1 && !disabled) { - return new OverrideAllowedState(DISABLED_NON_TARGET_SDK, appTargetSdk, minTargetSdk); + if (maxTargetSdk == -1 && !disabled) { + return new OverrideAllowedState(DISABLED_NON_TARGET_SDK, appTargetSdk, maxTargetSdk); } // Only allow to opt-in for a targetSdk gated change. - if (disabled || appTargetSdk <= minTargetSdk) { - return new OverrideAllowedState(ALLOWED, appTargetSdk, minTargetSdk); + if (disabled || appTargetSdk <= maxTargetSdk) { + return new OverrideAllowedState(ALLOWED, appTargetSdk, maxTargetSdk); } - return new OverrideAllowedState(DISABLED_TARGET_SDK_TOO_HIGH, appTargetSdk, minTargetSdk); + return new OverrideAllowedState(DISABLED_TARGET_SDK_TOO_HIGH, appTargetSdk, maxTargetSdk); } } diff --git a/services/core/java/com/android/server/compat/PlatformCompat.java b/services/core/java/com/android/server/compat/PlatformCompat.java index c7de8d3d585f..e4f52f1fc927 100644 --- a/services/core/java/com/android/server/compat/PlatformCompat.java +++ b/services/core/java/com/android/server/compat/PlatformCompat.java @@ -59,8 +59,8 @@ public class PlatformCompat extends IPlatformCompat.Stub { private final ChangeReporter mChangeReporter; private final CompatConfig mCompatConfig; - private static int sMinTargetSdk = Build.VERSION_CODES.P; - private static int sMaxTargetSdk = Build.VERSION_CODES.Q; + private static int sMinTargetSdk = Build.VERSION_CODES.Q; + private static int sMaxTargetSdk = Build.VERSION_CODES.R; public PlatformCompat(Context context) { mContext = context; @@ -381,9 +381,9 @@ public class PlatformCompat extends IPlatformCompat.Stub { if (change.getLoggingOnly()) { return false; } - if (change.getEnableAfterTargetSdk() > 0) { - if (change.getEnableAfterTargetSdk() < sMinTargetSdk - || change.getEnableAfterTargetSdk() > sMaxTargetSdk) { + if (change.getEnableSinceTargetSdk() > 0) { + if (change.getEnableSinceTargetSdk() < sMinTargetSdk + || change.getEnableSinceTargetSdk() > sMaxTargetSdk) { return false; } } diff --git a/services/core/java/com/android/server/connectivity/DnsManager.java b/services/core/java/com/android/server/connectivity/DnsManager.java index cf6a7f6e8d70..c7891865644a 100644 --- a/services/core/java/com/android/server/connectivity/DnsManager.java +++ b/services/core/java/com/android/server/connectivity/DnsManager.java @@ -50,7 +50,6 @@ import android.util.Slog; import java.net.InetAddress; import java.util.Arrays; -import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; @@ -238,24 +237,21 @@ public class DnsManager { private final Context mContext; private final ContentResolver mContentResolver; private final IDnsResolver mDnsResolver; - private final MockableSystemProperties mSystemProperties; private final ConcurrentHashMap<Integer, PrivateDnsConfig> mPrivateDnsMap; // TODO: Replace the Map with SparseArrays. private final Map<Integer, PrivateDnsValidationStatuses> mPrivateDnsValidationMap; private final Map<Integer, LinkProperties> mLinkPropertiesMap; private final Map<Integer, int[]> mTransportsMap; - private int mNumDnsEntries; private int mSampleValidity; private int mSuccessThreshold; private int mMinSamples; private int mMaxSamples; - public DnsManager(Context ctx, IDnsResolver dnsResolver, MockableSystemProperties sp) { + public DnsManager(Context ctx, IDnsResolver dnsResolver) { mContext = ctx; mContentResolver = mContext.getContentResolver(); mDnsResolver = dnsResolver; - mSystemProperties = sp; mPrivateDnsMap = new ConcurrentHashMap<>(); mPrivateDnsValidationMap = new HashMap<>(); mLinkPropertiesMap = new HashMap<>(); @@ -409,18 +405,6 @@ public class DnsManager { } } - 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; - } - /** * Flush DNS caches and events work before boot has completed. */ @@ -476,16 +460,6 @@ public class DnsManager { 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) { String mode = getStringSetting(cr, PRIVATE_DNS_MODE); if (TextUtils.isEmpty(mode)) mode = getStringSetting(cr, PRIVATE_DNS_DEFAULT_MODE); diff --git a/services/core/java/com/android/server/connectivity/MockableSystemProperties.java b/services/core/java/com/android/server/connectivity/MockableSystemProperties.java index 77b86d8e4355..ef767341d609 100644 --- a/services/core/java/com/android/server/connectivity/MockableSystemProperties.java +++ b/services/core/java/com/android/server/connectivity/MockableSystemProperties.java @@ -17,6 +17,7 @@ package com.android.server.connectivity; import android.os.SystemProperties; +import android.sysprop.NetworkProperties; public class MockableSystemProperties { @@ -31,8 +32,10 @@ public class MockableSystemProperties { public boolean getBoolean(String key, boolean def) { return SystemProperties.getBoolean(key, def); } - - public void set(String key, String value) { - SystemProperties.set(key, value); + /** + * Set net.tcp_def_init_rwnd to the tcp initial receive window size. + */ + public void setTcpInitRwnd(int value) { + NetworkProperties.tcp_init_rwnd(value); } } diff --git a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java index 4cff5c013bda..7e3c1ab50ad5 100644 --- a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java +++ b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java @@ -16,8 +16,11 @@ package com.android.server.devicestate; +import static android.hardware.devicestate.DeviceStateManager.INVALID_DEVICE_STATE; + import android.annotation.NonNull; import android.content.Context; +import android.hardware.devicestate.IDeviceStateManager; import android.util.IntArray; import android.util.Slog; @@ -49,9 +52,6 @@ import java.util.Arrays; * @see DeviceStatePolicy */ public final class DeviceStateManagerService extends SystemService { - /** Invalid device state. */ - public static final int INVALID_DEVICE_STATE = -1; - private static final String TAG = "DeviceStateManagerService"; private static final boolean DEBUG = false; @@ -88,6 +88,7 @@ public final class DeviceStateManagerService extends SystemService { @Override public void onStart() { mDeviceStatePolicy.getDeviceStateProvider().setListener(new DeviceStateProviderListener()); + publishBinderService(Context.DEVICE_STATE_SERVICE, new BinderService()); } /** @@ -267,4 +268,9 @@ public final class DeviceStateManagerService extends SystemService { requestState(state); } } + + /** Implementation of {@link IDeviceStateManager} published as a binder service. */ + private final class BinderService extends IDeviceStateManager.Stub { + + } } diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java index eb2c7e6105ae..93cada7adca3 100644 --- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java +++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java @@ -208,7 +208,6 @@ class AutomaticBrightnessController { private PackageManager mPackageManager; private Context mContext; - private DisplayDeviceConfig mDisplayDeviceConfig; private final Injector mInjector; AutomaticBrightnessController(Callbacks callbacks, Looper looper, @@ -217,14 +216,13 @@ class AutomaticBrightnessController { float dozeScaleFactor, int lightSensorRate, int initialLightSensorRate, long brighteningLightDebounceConfig, long darkeningLightDebounceConfig, boolean resetAmbientLuxAfterWarmUpConfig, HysteresisLevels ambientBrightnessThresholds, - HysteresisLevels screenBrightnessThresholds, Context context, DisplayDeviceConfig - displayDeviceConfig) { + HysteresisLevels screenBrightnessThresholds, Context context) { this(new Injector(), callbacks, looper, sensorManager, lightSensor, mapper, lightSensorWarmUpTime, brightnessMin, brightnessMax, dozeScaleFactor, lightSensorRate, initialLightSensorRate, brighteningLightDebounceConfig, darkeningLightDebounceConfig, resetAmbientLuxAfterWarmUpConfig, - ambientBrightnessThresholds, screenBrightnessThresholds, context, - displayDeviceConfig); + ambientBrightnessThresholds, screenBrightnessThresholds, context + ); } @VisibleForTesting @@ -234,8 +232,7 @@ class AutomaticBrightnessController { float dozeScaleFactor, int lightSensorRate, int initialLightSensorRate, long brighteningLightDebounceConfig, long darkeningLightDebounceConfig, boolean resetAmbientLuxAfterWarmUpConfig, HysteresisLevels ambientBrightnessThresholds, - HysteresisLevels screenBrightnessThresholds, Context context, DisplayDeviceConfig - displayDeviceConfig) { + HysteresisLevels screenBrightnessThresholds, Context context) { mInjector = injector; mContext = context; mCallbacks = callbacks; @@ -257,7 +254,6 @@ class AutomaticBrightnessController { mScreenBrightnessThresholds = screenBrightnessThresholds; mShortTermModelValid = true; mShortTermModelAnchor = -1; - mDisplayDeviceConfig = displayDeviceConfig; mHandler = new AutomaticBrightnessHandler(looper); mAmbientLightRingBuffer = new AmbientLightRingBuffer(mNormalLightSensorRate, mAmbientLightHorizon); diff --git a/services/core/java/com/android/server/display/DisplayBlanker.java b/services/core/java/com/android/server/display/DisplayBlanker.java index d294898da8c4..e2129ba13626 100644 --- a/services/core/java/com/android/server/display/DisplayBlanker.java +++ b/services/core/java/com/android/server/display/DisplayBlanker.java @@ -20,5 +20,5 @@ package com.android.server.display; * Interface used to update the actual display state. */ public interface DisplayBlanker { - void requestDisplayState(int state, float brightness); + void requestDisplayState(int displayId, int state, float brightness); } diff --git a/services/core/java/com/android/server/display/DisplayDeviceRepository.java b/services/core/java/com/android/server/display/DisplayDeviceRepository.java index aa4db29e0d49..5c0fceb9b795 100644 --- a/services/core/java/com/android/server/display/DisplayDeviceRepository.java +++ b/services/core/java/com/android/server/display/DisplayDeviceRepository.java @@ -112,6 +112,17 @@ class DisplayDeviceRepository implements DisplayAdapter.Listener { } } + public DisplayDevice getByIdLocked(@NonNull String uniqueId) { + final int count = mDisplayDevices.size(); + for (int i = 0; i < count; i++) { + final DisplayDevice d = mDisplayDevices.get(i); + if (uniqueId.equals(d.getUniqueId())) { + return d; + } + } + return null; + } + private void handleDisplayDeviceAdded(DisplayDevice device) { synchronized (mSyncRoot) { DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked(); diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java index ca94efc3e5a0..60c83905ed9c 100644 --- a/services/core/java/com/android/server/display/DisplayManagerService.java +++ b/services/core/java/com/android/server/display/DisplayManagerService.java @@ -91,6 +91,7 @@ import android.util.SparseArray; import android.util.Spline; import android.view.Display; import android.view.DisplayInfo; +import android.view.IDisplayFoldListener; import android.view.Surface; import android.view.SurfaceControl; @@ -103,6 +104,7 @@ import com.android.server.DisplayThread; import com.android.server.LocalServices; import com.android.server.SystemService; import com.android.server.UiThread; +import com.android.server.policy.WindowManagerPolicy; import com.android.server.wm.SurfaceAnimationThread; import com.android.server.wm.WindowManagerInternal; @@ -312,6 +314,9 @@ public final class DisplayManagerService extends SystemService { // Receives notifications about changes to Settings. private SettingsObserver mSettingsObserver; + // Received notifications of the display-fold action + private DisplayFoldListener mDisplayFoldListener; + public DisplayManagerService(Context context) { this(context, new Injector()); } @@ -324,7 +329,7 @@ public final class DisplayManagerService extends SystemService { mHandler = new DisplayManagerHandler(DisplayThread.get().getLooper()); mUiHandler = UiThread.getHandler(); mDisplayDeviceRepo = new DisplayDeviceRepository(mSyncRoot, mPersistentDataStore); - mLogicalDisplayMapper = new LogicalDisplayMapper(mDisplayDeviceRepo, + mLogicalDisplayMapper = new LogicalDisplayMapper(context, mDisplayDeviceRepo, new LogicalDisplayListener(), mPersistentDataStore); mDisplayModeDirector = new DisplayModeDirector(context, mHandler); Resources resources = mContext.getResources(); @@ -429,6 +434,11 @@ public final class DisplayManagerService extends SystemService { synchronized (mSyncRoot) { mWindowManagerInternal = LocalServices.getService(WindowManagerInternal.class); mInputManagerInternal = LocalServices.getService(InputManagerInternal.class); + WindowManagerPolicy policy = LocalServices.getService(WindowManagerPolicy.class); + + mDisplayFoldListener = new DisplayFoldListener(); + policy.registerDisplayFoldListener(mDisplayFoldListener); + scheduleTraversalLocked(false); } } @@ -576,6 +586,7 @@ public final class DisplayManagerService extends SystemService { Trace.traceBegin(Trace.TRACE_TAG_POWER, "requestGlobalDisplayState(" + Display.stateToString(state) + ", brightness=" + brightnessState + ")"); + mGlobalDisplayState = state; mGlobalDisplayBrightness = brightnessState; applyGlobalDisplayStateLocked(mTempDisplayStateWorkQueue); @@ -983,6 +994,15 @@ public final class DisplayManagerService extends SystemService { scheduleTraversalLocked(false); } + private void handleLogicalDisplaySwappedLocked(@NonNull LogicalDisplay display) { + final DisplayDevice device = display.getPrimaryDisplayDeviceLocked(); + final Runnable work = updateDisplayStateLocked(device); + if (work != null) { + mHandler.post(work); + } + handleLogicalDisplayChangedLocked(display); + } + private void applyGlobalDisplayStateLocked(List<Runnable> workQueue) { mDisplayDeviceRepo.forEachLocked((DisplayDevice device) -> { Runnable runnable = updateDisplayStateLocked(device); @@ -997,10 +1017,15 @@ public final class DisplayManagerService extends SystemService { // by the display power controller (if known). DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked(); if ((info.flags & DisplayDeviceInfo.FLAG_NEVER_BLANK) == 0) { - // TODO - multi-display - The rules regarding what display state to apply to each + // TODO - b/170498827 The rules regarding what display state to apply to each // display will depend on the configuration/mapping of logical displays. - return device.requestDisplayStateLocked( - mGlobalDisplayState, mGlobalDisplayBrightness); + // Clean up LogicalDisplay.isEnabled() mechanism once this is fixed. + int state = mGlobalDisplayState; + final LogicalDisplay display = mLogicalDisplayMapper.getLocked(device); + if (display != null && !display.isEnabled()) { + state = Display.STATE_OFF; + } + return device.requestDisplayStateLocked(state, mGlobalDisplayBrightness); } return null; } @@ -1346,6 +1371,12 @@ public final class DisplayManagerService extends SystemService { } } + void setFoldOverride(Boolean isFolded) { + synchronized (mSyncRoot) { + mLogicalDisplayMapper.setFoldOverrideLocked(isFolded); + } + } + private void clearViewportsLocked() { mViewports.clear(); } @@ -1698,6 +1729,10 @@ public final class DisplayManagerService extends SystemService { case LogicalDisplayMapper.LOGICAL_DISPLAY_EVENT_REMOVED: handleLogicalDisplayRemovedLocked(display); break; + + case LogicalDisplayMapper.LOGICAL_DISPLAY_EVENT_SWAPPED: + handleLogicalDisplaySwappedLocked(display); + break; } } @@ -2382,7 +2417,7 @@ public final class DisplayManagerService extends SystemService { synchronized (mSyncRoot) { DisplayBlanker blanker = new DisplayBlanker() { @Override - public void requestDisplayState(int state, float brightness) { + public void requestDisplayState(int displayId, int state, float brightness) { // The order of operations is important for legacy reasons. if (state == Display.STATE_OFF) { requestGlobalDisplayStateInternal(state, brightness); @@ -2395,11 +2430,9 @@ public final class DisplayManagerService extends SystemService { } } }; - LogicalDisplay defaultDisplay = - mLogicalDisplayMapper.getLocked(Display.DEFAULT_DISPLAY); - DisplayDevice defaultDevice = defaultDisplay.getPrimaryDisplayDeviceLocked(); mDisplayPowerController = new DisplayPowerController( - mContext, callbacks, handler, sensorManager, blanker, defaultDevice); + mContext, callbacks, handler, sensorManager, blanker, + Display.DEFAULT_DISPLAY); mSensorManager = sensorManager; } @@ -2576,4 +2609,17 @@ public final class DisplayManagerService extends SystemService { } } } + + class DisplayFoldListener extends IDisplayFoldListener.Stub { + @Override + public void onDisplayFoldChanged(int displayId, boolean folded) { + // TODO: multi-display - IDisplayFoldListener callback only really works for the + // Display.DEFAULT_DISPLAY. + if (displayId == Display.DEFAULT_DISPLAY) { + synchronized (mSyncRoot) { + mLogicalDisplayMapper.setDeviceFoldedLocked(folded); + } + } + } + }; } diff --git a/services/core/java/com/android/server/display/DisplayManagerShellCommand.java b/services/core/java/com/android/server/display/DisplayManagerShellCommand.java index 0c6c797b917a..111664a078df 100644 --- a/services/core/java/com/android/server/display/DisplayManagerShellCommand.java +++ b/services/core/java/com/android/server/display/DisplayManagerShellCommand.java @@ -56,6 +56,8 @@ class DisplayManagerShellCommand extends ShellCommand { return setDisplayWhiteBalanceLoggingEnabled(false); case "dwb-set-cct": return setAmbientColorTemperatureOverride(); + case "set-fold": + return setFoldOverride(); default: return handleDefaultCommands(cmd); } @@ -82,6 +84,8 @@ class DisplayManagerShellCommand extends ShellCommand { pw.println(" Disable display white-balance logging."); pw.println(" dwb-set-cct CCT"); pw.println(" Sets the ambient color temperature override to CCT (use -1 to disable)."); + pw.println(" set-fold [fold|unfold|reset]"); + pw.println(" Simulates the 'fold' state of a device. 'reset' for default behavior."); pw.println(); Intent.printIntentArgsHelp(pw , ""); } @@ -148,4 +152,26 @@ class DisplayManagerShellCommand extends ShellCommand { mService.setAmbientColorTemperatureOverride(cct); return 0; } + + private int setFoldOverride() { + String state = getNextArg(); + if (state == null) { + getErrPrintWriter().println("Error: no parameter specified for set-fold"); + return 1; + } + final Boolean isFolded; + if ("fold".equals(state)) { + isFolded = true; + } else if ("unfold".equals(state)) { + isFolded = false; + } else if ("reset".equals(state)) { + isFolded = null; + } else { + getErrPrintWriter().println("Error: Invalid fold state request: " + state); + return 1; + } + + mService.setFoldOverride(isFolded); + return 0; + } } diff --git a/services/core/java/com/android/server/display/DisplayModeDirector.java b/services/core/java/com/android/server/display/DisplayModeDirector.java index 4f5a0faee8dd..71b377cc2f11 100644 --- a/services/core/java/com/android/server/display/DisplayModeDirector.java +++ b/services/core/java/com/android/server/display/DisplayModeDirector.java @@ -123,7 +123,6 @@ public class DisplayModeDirector { public void start(SensorManager sensorManager) { mSettingsObserver.observe(); mDisplayObserver.observe(); - mSettingsObserver.observe(); mBrightnessObserver.observe(sensorManager); synchronized (mLock) { // We may have a listener already registered before the call to start, so go ahead and diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java index 58ef9d11ef97..0211876c864d 100644 --- a/services/core/java/com/android/server/display/DisplayPowerController.java +++ b/services/core/java/com/android/server/display/DisplayPowerController.java @@ -163,8 +163,8 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call // The display blanker. private final DisplayBlanker mBlanker; - // The display device. - private final DisplayDevice mDisplayDevice; + // The ID of the LogicalDisplay tied to this DisplayPowerController. + private final int mDisplayId; // Tracker for brightness changes. private final BrightnessTracker mBrightnessTracker; @@ -406,7 +406,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call */ public DisplayPowerController(Context context, DisplayPowerCallbacks callbacks, Handler handler, - SensorManager sensorManager, DisplayBlanker blanker, DisplayDevice displayDevice) { + SensorManager sensorManager, DisplayBlanker blanker, int displayId) { mHandler = new DisplayControllerHandler(handler.getLooper()); mBrightnessTracker = new BrightnessTracker(context, null); mSettingsObserver = new SettingsObserver(mHandler); @@ -417,10 +417,9 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call mBlanker = blanker; mContext = context; mBrightnessSynchronizer = new BrightnessSynchronizer(context); - mDisplayDevice = displayDevice; + mDisplayId = displayId; PowerManager pm = context.getSystemService(PowerManager.class); - DisplayDeviceConfig displayDeviceConfig = mDisplayDevice.getDisplayDeviceConfig(); final Resources resources = context.getResources(); @@ -515,7 +514,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call mScreenBrightnessRangeMaximum, dozeScaleFactor, lightSensorRate, initialLightSensorRate, brighteningLightDebounce, darkeningLightDebounce, autoBrightnessResetAmbientLuxAfterWarmUp, ambientBrightnessThresholds, - screenBrightnessThresholds, context, displayDeviceConfig); + screenBrightnessThresholds, context); } else { mUseSoftwareAutoBrightnessConfig = false; } @@ -684,7 +683,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call // Initialize the power state object for the default display. // In the future, we might manage multiple displays independently. mPowerState = new DisplayPowerState(mBlanker, - mColorFadeEnabled ? new ColorFade(Display.DEFAULT_DISPLAY) : null); + mColorFadeEnabled ? new ColorFade(mDisplayId) : null, mDisplayId); if (mColorFadeEnabled) { mColorFadeOnAnimator = ObjectAnimator.ofFloat( @@ -1153,7 +1152,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call if (ready && state != Display.STATE_OFF && mReportedScreenStateToPolicy == REPORTED_TO_POLICY_SCREEN_TURNING_ON) { setReportedScreenState(REPORTED_TO_POLICY_SCREEN_ON); - mWindowManagerPolicy.screenTurnedOn(); + mWindowManagerPolicy.screenTurnedOn(mDisplayId); } // Grab a wake lock if we have unfinished business. @@ -1277,7 +1276,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call if (mReportedScreenStateToPolicy == REPORTED_TO_POLICY_SCREEN_ON) { setReportedScreenState(REPORTED_TO_POLICY_SCREEN_TURNING_OFF); blockScreenOff(); - mWindowManagerPolicy.screenTurningOff(mPendingScreenOffUnblocker); + mWindowManagerPolicy.screenTurningOff(mDisplayId, mPendingScreenOffUnblocker); unblockScreenOff(); } else if (mPendingScreenOffUnblocker != null) { // Abort doing the state change until screen off is unblocked. @@ -1309,14 +1308,14 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call && !mScreenOffBecauseOfProximity) { setReportedScreenState(REPORTED_TO_POLICY_SCREEN_OFF); unblockScreenOn(); - mWindowManagerPolicy.screenTurnedOff(); + mWindowManagerPolicy.screenTurnedOff(mDisplayId); } else if (!isOff && mReportedScreenStateToPolicy == REPORTED_TO_POLICY_SCREEN_TURNING_OFF) { // We told policy already that screen was turning off, but now we changed our minds. // Complete the full state transition on -> turningOff -> off. unblockScreenOff(); - mWindowManagerPolicy.screenTurnedOff(); + mWindowManagerPolicy.screenTurnedOff(mDisplayId); setReportedScreenState(REPORTED_TO_POLICY_SCREEN_OFF); } if (!isOff && mReportedScreenStateToPolicy == REPORTED_TO_POLICY_SCREEN_OFF) { @@ -1326,7 +1325,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call } else { unblockScreenOn(); } - mWindowManagerPolicy.screenTurningOn(mPendingScreenOnUnblocker); + mWindowManagerPolicy.screenTurningOn(mDisplayId, mPendingScreenOnUnblocker); } // Return true if the screen isn't blocked. diff --git a/services/core/java/com/android/server/display/DisplayPowerState.java b/services/core/java/com/android/server/display/DisplayPowerState.java index 4b6430d5197c..54f30a954c33 100644 --- a/services/core/java/com/android/server/display/DisplayPowerState.java +++ b/services/core/java/com/android/server/display/DisplayPowerState.java @@ -58,6 +58,7 @@ final class DisplayPowerState { private final DisplayBlanker mBlanker; private final ColorFade mColorFade; private final PhotonicModulator mPhotonicModulator; + private final int mDisplayId; private int mScreenState; private float mScreenBrightness; @@ -71,13 +72,14 @@ final class DisplayPowerState { private Runnable mCleanListener; - public DisplayPowerState(DisplayBlanker blanker, ColorFade colorFade) { + public DisplayPowerState(DisplayBlanker blanker, ColorFade colorFade, int displayId) { mHandler = new Handler(true /*async*/); mChoreographer = Choreographer.getInstance(); mBlanker = blanker; mColorFade = colorFade; mPhotonicModulator = new PhotonicModulator(); mPhotonicModulator.start(); + mDisplayId = displayId; // At boot time, we know that the screen is on and the electron beam // animation is not playing. We don't know the screen's brightness though, @@ -434,10 +436,10 @@ final class DisplayPowerState { // Apply pending change. if (DEBUG) { - Slog.d(TAG, "Updating screen state: state=" + Slog.d(TAG, "Updating screen state: id=" + mDisplayId + ", state=" + Display.stateToString(state) + ", backlight=" + brightnessState); } - mBlanker.requestDisplayState(state, brightnessState); + mBlanker.requestDisplayState(mDisplayId, state, brightnessState); } } } diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java index 858cce49d1c1..6597aa54d66b 100644 --- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java +++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java @@ -69,21 +69,27 @@ final class LocalDisplayAdapter extends DisplayAdapter { private final LongSparseArray<LocalDisplayDevice> mDevices = new LongSparseArray<>(); - @SuppressWarnings("unused") // Becomes active at instantiation time. - private PhysicalDisplayEventReceiver mPhysicalDisplayEventReceiver; + private final Injector mInjector; // Called with SyncRoot lock held. public LocalDisplayAdapter(DisplayManagerService.SyncRoot syncRoot, Context context, Handler handler, Listener listener) { + this(syncRoot, context, handler, listener, new Injector()); + } + + LocalDisplayAdapter(DisplayManagerService.SyncRoot syncRoot, + Context context, Handler handler, Listener listener, Injector injector) { super(syncRoot, context, handler, listener, TAG); + mInjector = injector; } @Override public void registerLocked() { super.registerLocked(); - mPhysicalDisplayEventReceiver = new PhysicalDisplayEventReceiver(getHandler().getLooper()); + mInjector.setDisplayEventListenerLocked(getHandler().getLooper(), + new LocalDisplayEventListener()); for (long physicalDisplayId : SurfaceControl.getPhysicalDisplayIds()) { tryConnectDisplayLocked(physicalDisplayId); @@ -183,13 +189,11 @@ final class LocalDisplayAdapter extends DisplayAdapter { private int mDefaultModeId; private int mDefaultConfigGroup; private int mActiveModeId; - private boolean mActiveModeInvalid; private DisplayModeDirector.DesiredDisplayModeSpecs mDisplayModeSpecs = new DisplayModeDirector.DesiredDisplayModeSpecs(); private boolean mDisplayModeSpecsInvalid; private int mActiveConfigId; private int mActiveColorMode; - private boolean mActiveColorModeInvalid; private Display.HdrCapabilities mHdrCapabilities; private boolean mAllmSupported; private boolean mGameContentTypeSupported; @@ -300,7 +304,6 @@ final class LocalDisplayAdapter extends DisplayAdapter { // Schedule traversals to ensure that the correct state is reapplied if necessary. if (mActiveModeId != NO_DISPLAY_MODE_ID && mActiveModeId != activeRecord.mMode.getModeId()) { - mActiveModeInvalid = true; sendTraversalRequestLocked(); } @@ -373,7 +376,6 @@ final class LocalDisplayAdapter extends DisplayAdapter { + " mode."); } mActiveModeId = mDefaultModeId; - mActiveModeInvalid = true; } // Schedule traversals so that we apply pending changes. @@ -464,14 +466,12 @@ final class LocalDisplayAdapter extends DisplayAdapter { Slog.w(TAG, "Active color mode no longer available, reverting" + " to default mode."); mActiveColorMode = Display.COLOR_MODE_DEFAULT; - mActiveColorModeInvalid = true; } else { if (!mSupportedColorModes.isEmpty()) { // This should never happen. Slog.e(TAG, "Default and active color mode is no longer available!" + " Reverting to first available mode."); mActiveColorMode = mSupportedColorModes.get(0); - mActiveColorModeInvalid = true; } else { // This should really never happen. Slog.e(TAG, "No color modes available!"); @@ -848,8 +848,7 @@ final class LocalDisplayAdapter extends DisplayAdapter { } mActiveConfigId = activeConfigId; mActiveModeId = findMatchingModeIdLocked(activeConfigId); - mActiveModeInvalid = mActiveModeId == NO_DISPLAY_MODE_ID; - if (mActiveModeInvalid) { + if (mActiveModeId == NO_DISPLAY_MODE_ID) { Slog.w(TAG, "In unknown mode after setting allowed configs" + ", activeConfigId=" + mActiveConfigId); } @@ -867,7 +866,6 @@ final class LocalDisplayAdapter extends DisplayAdapter { } mActiveColorMode = colorMode; - mActiveColorModeInvalid = false; getHandler().sendMessage(PooledLambda.obtainMessage( LocalDisplayDevice::requestColorModeAsync, this, getDisplayTokenLocked(), colorMode)); @@ -1052,12 +1050,33 @@ final class LocalDisplayAdapter extends DisplayAdapter { } } - private final class PhysicalDisplayEventReceiver extends DisplayEventReceiver { - PhysicalDisplayEventReceiver(Looper looper) { + public static class Injector { + private ProxyDisplayEventReceiver mReceiver; + public void setDisplayEventListenerLocked(Looper looper, DisplayEventListener listener) { + mReceiver = new ProxyDisplayEventReceiver(looper, listener); + } + } + + public interface DisplayEventListener { + void onHotplug(long timestampNanos, long physicalDisplayId, boolean connected); + void onConfigChanged(long timestampNanos, long physicalDisplayId, int configId); + } + + public static final class ProxyDisplayEventReceiver extends DisplayEventReceiver { + private final DisplayEventListener mListener; + ProxyDisplayEventReceiver(Looper looper, DisplayEventListener listener) { super(looper, VSYNC_SOURCE_APP, CONFIG_CHANGED_EVENT_DISPATCH); + mListener = listener; } + public void onHotplug(long timestampNanos, long physicalDisplayId, boolean connected) { + mListener.onHotplug(timestampNanos, physicalDisplayId, connected); + } + public void onConfigChanged(long timestampNanos, long physicalDisplayId, int configId) { + mListener.onConfigChanged(timestampNanos, physicalDisplayId, configId); + } + } - @Override + private final class LocalDisplayEventListener implements DisplayEventListener { public void onHotplug(long timestampNanos, long physicalDisplayId, boolean connected) { synchronized (getSyncRoot()) { if (connected) { @@ -1067,8 +1086,6 @@ final class LocalDisplayAdapter extends DisplayAdapter { } } } - - @Override public void onConfigChanged(long timestampNanos, long physicalDisplayId, int configId) { if (DEBUG) { Slog.d(TAG, "onConfigChanged(" diff --git a/services/core/java/com/android/server/display/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java index bf8b891cffb8..a17a294cd1d7 100644 --- a/services/core/java/com/android/server/display/LogicalDisplay.java +++ b/services/core/java/com/android/server/display/LogicalDisplay.java @@ -16,9 +16,11 @@ package com.android.server.display; +import android.annotation.NonNull; import android.graphics.Point; import android.graphics.Rect; import android.hardware.display.DisplayManagerInternal; +import android.util.Slog; import android.view.Display; import android.view.DisplayInfo; import android.view.Surface; @@ -57,6 +59,7 @@ import java.util.Objects; * </p> */ final class LogicalDisplay { + private static final String TAG = "LogicalDisplay"; private final DisplayInfo mBaseDisplayInfo = new DisplayInfo(); // The layer stack we use when the display has been blanked to prevent any @@ -114,6 +117,12 @@ final class LogicalDisplay { private final Rect mTempLayerStackRect = new Rect(); private final Rect mTempDisplayRect = new Rect(); + /** + * Indicates that the Logical display is enabled (default). See {@link #setEnabled} for + * more information. + */ + private boolean mIsEnabled = true; + public LogicalDisplay(int displayId, int layerStack, DisplayDevice primaryDisplayDevice) { mDisplayId = displayId; mLayerStack = layerStack; @@ -575,6 +584,44 @@ final class LogicalDisplay { mDisplayScalingDisabled = disableScaling; } + /** + * Swap the underlying {@link DisplayDevice} with the specificed LogicalDisplay. + * + * @param targetDisplay The display with which to swap display-devices. + * @return {@code true} if the displays were swapped, {@code false} otherwise. + */ + public boolean swapDisplaysLocked(@NonNull LogicalDisplay targetDisplay) { + final DisplayDevice targetDevice = targetDisplay.getPrimaryDisplayDeviceLocked(); + if (mPrimaryDisplayDevice == null || targetDevice == null) { + Slog.e(TAG, "Missing display device during swap: " + mPrimaryDisplayDevice + " , " + + targetDevice); + return false; + } + + final DisplayDevice tmpDevice = mPrimaryDisplayDevice; + mPrimaryDisplayDevice = targetDisplay.mPrimaryDisplayDevice; + targetDisplay.mPrimaryDisplayDevice = tmpDevice; + return true; + } + + /** + * Sets the LogicalDisplay to be enabled or disabled. If the display is not enabled, + * the system will always set the display to power off, regardless of the global state of the + * device. + * TODO: b/170498827 - Remove when updateDisplayStateLocked is updated. + */ + public void setEnabled(boolean isEnabled) { + mIsEnabled = isEnabled; + } + + /** + * @return {@code true} iff the LogicalDisplay is enabled or {@code false} + * if disabled indicating that the display has been forced to be OFF. + */ + public boolean isEnabled() { + return mIsEnabled; + } + public void dumpLocked(PrintWriter pw) { pw.println("mDisplayId=" + mDisplayId); pw.println("mLayerStack=" + mLayerStack); diff --git a/services/core/java/com/android/server/display/LogicalDisplayMapper.java b/services/core/java/com/android/server/display/LogicalDisplayMapper.java index 8a90a142a765..fc3ba35c52ba 100644 --- a/services/core/java/com/android/server/display/LogicalDisplayMapper.java +++ b/services/core/java/com/android/server/display/LogicalDisplayMapper.java @@ -16,7 +16,9 @@ package com.android.server.display; +import android.content.Context; import android.os.SystemProperties; +import android.text.TextUtils; import android.util.Slog; import android.util.SparseArray; import android.view.Display; @@ -26,6 +28,7 @@ import com.android.internal.util.IndentingPrintWriter; import java.io.PrintWriter; import java.util.Arrays; +import java.util.Objects; import java.util.function.Consumer; /** @@ -39,9 +42,12 @@ import java.util.function.Consumer; class LogicalDisplayMapper implements DisplayDeviceRepository.Listener { private static final String TAG = "LogicalDisplayMapper"; + private static final boolean DEBUG = false; + public static final int LOGICAL_DISPLAY_EVENT_ADDED = 1; public static final int LOGICAL_DISPLAY_EVENT_CHANGED = 2; public static final int LOGICAL_DISPLAY_EVENT_REMOVED = 3; + public static final int LOGICAL_DISPLAY_EVENT_SWAPPED = 4; /** * Temporary display info, used for comparing display configurations. @@ -59,6 +65,24 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener { private final boolean mSingleDisplayDemoMode; /** + * Physical Display ID of the DisplayDevice to associate with the default LogicalDisplay + * when {@link mIsFolded} is set to {@code true}. + */ + private String mDisplayIdToUseWhenFolded; + + /** + * Physical Display ID of the DisplayDevice to associate with the default LogicalDisplay + * when {@link mIsFolded} is set to {@code false}. + */ + private String mDisplayIdToUseWhenUnfolded; + + /** Overrides the folded state of the device. For use with ADB commands. */ + private Boolean mIsFoldedOverride; + + /** Saves the last device fold state. */ + private boolean mIsFolded; + + /** * List of all logical displays indexed by logical display id. * Any modification to mLogicalDisplays must invalidate the DisplayManagerGlobal cache. * TODO: multi-display - Move the aforementioned comment? @@ -71,13 +95,15 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener { private final PersistentDataStore mPersistentDataStore; private final Listener mListener; - LogicalDisplayMapper(DisplayDeviceRepository repo, Listener listener, + LogicalDisplayMapper(Context context, DisplayDeviceRepository repo, Listener listener, PersistentDataStore persistentDataStore) { mDisplayDeviceRepo = repo; mPersistentDataStore = persistentDataStore; mListener = listener; mSingleDisplayDemoMode = SystemProperties.getBoolean("persist.demo.singledisplay", false); mDisplayDeviceRepo.addListener(this); + + loadFoldedDisplayConfig(context); } @Override @@ -143,21 +169,102 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener { public void dumpLocked(PrintWriter pw) { pw.println("LogicalDisplayMapper:"); - pw.println(" mSingleDisplayDemoMode=" + mSingleDisplayDemoMode); - pw.println(" mNextNonDefaultDisplayId=" + mNextNonDefaultDisplayId); + IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " "); + ipw.increaseIndent(); + + ipw.println("mSingleDisplayDemoMode=" + mSingleDisplayDemoMode); + ipw.println("mNextNonDefaultDisplayId=" + mNextNonDefaultDisplayId); final int logicalDisplayCount = mLogicalDisplays.size(); - pw.println(); - pw.println(" Logical Displays: size=" + logicalDisplayCount); + ipw.println(); + ipw.println("Logical Displays: size=" + logicalDisplayCount); - IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " "); - ipw.increaseIndent(); for (int i = 0; i < logicalDisplayCount; i++) { int displayId = mLogicalDisplays.keyAt(i); LogicalDisplay display = mLogicalDisplays.valueAt(i); - pw.println(" Display " + displayId + ":"); + ipw.println("Display " + displayId + ":"); + ipw.increaseIndent(); display.dumpLocked(ipw); + ipw.decreaseIndent(); + ipw.println(); + } + } + + void setDeviceFoldedLocked(boolean isFolded) { + mIsFolded = isFolded; + if (mIsFoldedOverride != null) { + isFolded = mIsFoldedOverride.booleanValue(); + } + + if (mDisplayIdToUseWhenFolded == null || mDisplayIdToUseWhenUnfolded == null + || mLogicalDisplays.size() < 2) { + // Do nothing if this behavior is disabled or there are less than two displays. + return; + } + + final DisplayDevice deviceFolded = + mDisplayDeviceRepo.getByIdLocked(mDisplayIdToUseWhenFolded); + final DisplayDevice deviceUnfolded = + mDisplayDeviceRepo.getByIdLocked(mDisplayIdToUseWhenUnfolded); + if (deviceFolded == null || deviceUnfolded == null) { + // If the expected devices for folding functionality are not present, return early. + return; + } + + // Find the associated LogicalDisplays for the configured "folding" DeviceDisplays. + final LogicalDisplay displayFolded = getLocked(deviceFolded); + final LogicalDisplay displayUnfolded = getLocked(deviceUnfolded); + if (displayFolded == null || displayFolded == null) { + // If the expected displays are not present, return early. + return; + } + + // Find out which display is currently default and which is disabled. + final LogicalDisplay defaultDisplay = mLogicalDisplays.get(Display.DEFAULT_DISPLAY); + final LogicalDisplay disabledDisplay; + if (defaultDisplay == displayFolded) { + disabledDisplay = displayUnfolded; + } else if (defaultDisplay == displayUnfolded) { + disabledDisplay = displayFolded; + } else { + // If neither folded or unfolded displays are currently set to the default display, we + // are in an unknown state and it's best to log the error and bail. + Slog.e(TAG, "Unexpected: when attempting to swap displays, neither of the two" + + " configured displays were set up as the default display. Default: " + + defaultDisplay.getDisplayInfoLocked() + ", ConfiguredDisplays: [ folded=" + + displayFolded.getDisplayInfoLocked() + ", unfolded=" + + displayUnfolded.getDisplayInfoLocked() + " ]"); + return; + } + + if (isFolded == (defaultDisplay == displayFolded)) { + // Nothing to do, already in the right state. + return; + } + + // Everything was checked and we need to swap, lets swap. + displayFolded.swapDisplaysLocked(displayUnfolded); + + // We ensure that the non-default Display is always forced to be off. This was likely + // already done in a previous iteration, but we do it with each swap in case something in + // the underlying LogicalDisplays changed: like LogicalDisplay recreation, for example. + defaultDisplay.setEnabled(true); + disabledDisplay.setEnabled(false); + + // Update the world + updateLogicalDisplaysLocked(); + + if (DEBUG) { + Slog.d(TAG, "Folded displays: isFolded: " + isFolded + ", defaultDisplay? " + + defaultDisplay.getDisplayInfoLocked()); + } + } + + void setFoldOverrideLocked(Boolean isFolded) { + if (!Objects.equals(isFolded, mIsFoldedOverride)) { + mIsFoldedOverride = isFolded; + setDeviceFoldedLocked(mIsFolded); } } @@ -211,8 +318,11 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener { mListener.onLogicalDisplayEventLocked(display, LogicalDisplayMapper.LOGICAL_DISPLAY_EVENT_REMOVED); } else if (!mTempDisplayInfo.equals(display.getDisplayInfoLocked())) { - mListener.onLogicalDisplayEventLocked(display, - LogicalDisplayMapper.LOGICAL_DISPLAY_EVENT_CHANGED); + final String oldUniqueId = mTempDisplayInfo.uniqueId; + final String newUniqueId = display.getDisplayInfoLocked().uniqueId; + final int eventMsg = TextUtils.equals(oldUniqueId, newUniqueId) + ? LOGICAL_DISPLAY_EVENT_CHANGED : LOGICAL_DISPLAY_EVENT_SWAPPED; + mListener.onLogicalDisplayEventLocked(display, eventMsg); } else { // While applications shouldn't know nor care about the non-overridden info, we // still need to let WindowManager know so it can update its own internal state for @@ -236,6 +346,21 @@ class LogicalDisplayMapper implements DisplayDeviceRepository.Listener { return displayId; } + private void loadFoldedDisplayConfig(Context context) { + final String[] displayIds = context.getResources().getStringArray( + com.android.internal.R.array.config_internalFoldedPhysicalDisplayIds); + + if (displayIds.length != 2 || TextUtils.isEmpty(displayIds[0]) + || TextUtils.isEmpty(displayIds[1])) { + Slog.w(TAG, "Folded display configuration invalid: [" + Arrays.toString(displayIds) + + "]"); + return; + } + + mDisplayIdToUseWhenFolded = displayIds[0]; + mDisplayIdToUseWhenUnfolded = displayIds[1]; + } + public interface Listener { void onLogicalDisplayEventLocked(LogicalDisplay display, int event); void onTraversalRequested(); diff --git a/services/core/java/com/android/server/location/ContextHubService.java b/services/core/java/com/android/server/location/ContextHubService.java index 264c611d45fe..fb23e0187ebf 100644 --- a/services/core/java/com/android/server/location/ContextHubService.java +++ b/services/core/java/com/android/server/location/ContextHubService.java @@ -17,7 +17,10 @@ package com.android.server.location; import android.app.PendingIntent; +import android.content.BroadcastReceiver; import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; import android.database.ContentObserver; import android.hardware.contexthub.V1_0.AsyncEventType; import android.hardware.contexthub.V1_0.ContextHub; @@ -26,8 +29,6 @@ import android.hardware.contexthub.V1_0.HubAppInfo; import android.hardware.contexthub.V1_0.IContexthubCallback; import android.hardware.contexthub.V1_0.Result; import android.hardware.contexthub.V1_0.TransactionResult; -import android.hardware.contexthub.V1_1.Setting; -import android.hardware.contexthub.V1_1.SettingValue; import android.hardware.location.ContextHubInfo; import android.hardware.location.ContextHubMessage; import android.hardware.location.ContextHubTransaction; @@ -43,6 +44,7 @@ import android.hardware.location.NanoAppInstanceInfo; import android.hardware.location.NanoAppMessage; import android.hardware.location.NanoAppState; import android.location.LocationManager; +import android.net.wifi.WifiManager; import android.os.Binder; import android.os.RemoteCallbackList; import android.os.RemoteException; @@ -111,6 +113,12 @@ public class ContextHubService extends IContextHubService.Stub { // The manager for the internal nanoapp state cache private final NanoAppStateManager mNanoAppStateManager = new NanoAppStateManager(); + // True if WiFi is available for the Context Hub + private boolean mIsWifiAvailable = false; + + // Lock object for sendWifiSettingUpdate() + private final Object mSendWifiSettingUpdateLock = new Object(); + /** * Class extending the callback to register with a Context Hub. */ @@ -196,7 +204,7 @@ public class ContextHubService extends IContextHubService.Stub { } mDefaultClientMap = Collections.unmodifiableMap(defaultClientMap); - if (mContextHubWrapper.supportsSettingNotifications()) { + if (mContextHubWrapper.supportsLocationSettingNotifications()) { sendLocationSettingUpdate(); mContext.getContentResolver().registerContentObserver( Settings.Secure.getUriFor(Settings.Secure.LOCATION_MODE), @@ -208,6 +216,32 @@ public class ContextHubService extends IContextHubService.Stub { } }, UserHandle.USER_ALL); } + + if (mContextHubWrapper.supportsWifiSettingNotifications()) { + sendWifiSettingUpdate(true /* forceUpdate */); + + BroadcastReceiver wifiReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if (WifiManager.WIFI_STATE_CHANGED_ACTION.equals(intent.getAction())) { + sendWifiSettingUpdate(false /* forceUpdate */); + } + } + }; + IntentFilter filter = new IntentFilter(); + filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION); + mContext.registerReceiver(wifiReceiver, filter); + + mContext.getContentResolver().registerContentObserver( + Settings.Global.getUriFor(Settings.Global.WIFI_SCAN_ALWAYS_AVAILABLE), + true /* notifyForDescendants */, + new ContentObserver(null /* handler */) { + @Override + public void onChange(boolean selfChange) { + sendWifiSettingUpdate(false /* forceUpdate */); + } + }, UserHandle.USER_ALL); + } } /** @@ -260,7 +294,10 @@ public class ContextHubService extends IContextHubService.Stub { * @return the IContextHubWrapper interface */ private IContextHubWrapper getContextHubWrapper() { - IContextHubWrapper wrapper = IContextHubWrapper.maybeConnectTo1_1(); + IContextHubWrapper wrapper = IContextHubWrapper.maybeConnectTo1_2(); + if (wrapper == null) { + wrapper = IContextHubWrapper.maybeConnectTo1_1(); + } if (wrapper == null) { wrapper = IContextHubWrapper.maybeConnectTo1_0(); } @@ -454,7 +491,6 @@ public class ContextHubService extends IContextHubService.Stub { * * @param contextHubId the ID of the hub to do the query * @return the result of the query - * * @throws IllegalStateException if the transaction queue is full */ private int queryNanoAppsInternal(int contextHubId) { @@ -528,7 +564,6 @@ public class ContextHubService extends IContextHubService.Stub { /** * A helper function to handle a load response from the Context Hub for the old API. - * * TODO(b/69270990): Remove this once the old APIs are obsolete. */ private void handleLoadResponseOldApi( @@ -578,6 +613,7 @@ public class ContextHubService extends IContextHubService.Stub { private void handleHubEventCallback(int contextHubId, int eventType) { if (eventType == AsyncEventType.RESTARTED) { sendLocationSettingUpdate(); + sendWifiSettingUpdate(true /* forceUpdate */); mTransactionManager.onHubReset(); queryNanoAppsInternal(contextHubId); @@ -628,10 +664,9 @@ public class ContextHubService extends IContextHubService.Stub { * @param contextHubId the ID of the hub this client is attached to * @param clientCallback the client interface to register with the service * @return the generated client interface, null if registration was unsuccessful - * * @throws IllegalArgumentException if contextHubId is not a valid ID - * @throws IllegalStateException if max number of clients have already registered - * @throws NullPointerException if clientCallback is null + * @throws IllegalStateException if max number of clients have already registered + * @throws NullPointerException if clientCallback is null */ @Override public IContextHubClient createClient( @@ -655,7 +690,6 @@ public class ContextHubService extends IContextHubService.Stub { * @param pendingIntent the PendingIntent associated with this client * @param nanoAppId the ID of the nanoapp PendingIntent events will be sent for * @return the generated client interface - * * @throws IllegalArgumentException if hubInfo does not represent a valid hub * @throws IllegalStateException if there were too many registered clients at the service */ @@ -677,7 +711,6 @@ public class ContextHubService extends IContextHubService.Stub { * @param contextHubId the ID of the hub to load the binary * @param transactionCallback the client-facing transaction callback interface * @param nanoAppBinary the binary to load - * * @throws IllegalStateException if the transaction queue is full */ @Override @@ -707,7 +740,6 @@ public class ContextHubService extends IContextHubService.Stub { * @param contextHubId the ID of the hub to unload the nanoapp * @param transactionCallback the client-facing transaction callback interface * @param nanoAppId the ID of the nanoapp to unload - * * @throws IllegalStateException if the transaction queue is full */ @Override @@ -731,7 +763,6 @@ public class ContextHubService extends IContextHubService.Stub { * @param contextHubId the ID of the hub to enable the nanoapp * @param transactionCallback the client-facing transaction callback interface * @param nanoAppId the ID of the nanoapp to enable - * * @throws IllegalStateException if the transaction queue is full */ @Override @@ -755,7 +786,6 @@ public class ContextHubService extends IContextHubService.Stub { * @param contextHubId the ID of the hub to disable the nanoapp * @param transactionCallback the client-facing transaction callback interface * @param nanoAppId the ID of the nanoapp to disable - * * @throws IllegalStateException if the transaction queue is full */ @Override @@ -778,7 +808,6 @@ public class ContextHubService extends IContextHubService.Stub { * * @param contextHubId the ID of the hub to query * @param transactionCallback the client-facing transaction callback interface - * * @throws IllegalStateException if the transaction queue is full */ @Override @@ -889,7 +918,6 @@ public class ContextHubService extends IContextHubService.Stub { * @param contextHubId the ID of the hub to start the transaction * @param callback the client transaction callback interface * @param transactionType the type of the transaction - * * @return {@code true} if mContextHubWrapper and contextHubId is valid, {@code false} otherwise */ private boolean checkHalProxyAndContextHubId( @@ -920,14 +948,28 @@ public class ContextHubService extends IContextHubService.Stub { } /** - * Obtains the latest location setting value and notifies the Contexthub. + * Obtains the latest location setting value and notifies the Context Hub. */ private void sendLocationSettingUpdate() { boolean enabled = mContext.getSystemService(LocationManager.class) .isLocationEnabledForUser(UserHandle.CURRENT); + mContextHubWrapper.onLocationSettingChanged(enabled); + } - mContextHubWrapper.onSettingChanged(Setting.LOCATION, - enabled ? SettingValue.ENABLED : SettingValue.DISABLED); + /** + * Obtains the latest WiFi availability setting value and notifies the Context Hub. + * @param forceUpdate True to force send update to the Context Hub, otherwise only send the + * update when the WiFi availability changes. + */ + private void sendWifiSettingUpdate(boolean forceUpdate) { + synchronized (mSendWifiSettingUpdateLock) { + WifiManager wifiManager = mContext.getSystemService(WifiManager.class); + boolean enabled = wifiManager.isWifiEnabled() || wifiManager.isScanAlwaysAvailable(); + if (forceUpdate || mIsWifiAvailable != enabled) { + mIsWifiAvailable = enabled; + mContextHubWrapper.onWifiSettingChanged(enabled); + } + } } private String getCallingPackageName() { diff --git a/services/core/java/com/android/server/location/IContextHubWrapper.java b/services/core/java/com/android/server/location/IContextHubWrapper.java index 79fa5c7b3a95..613964a5ed14 100644 --- a/services/core/java/com/android/server/location/IContextHubWrapper.java +++ b/services/core/java/com/android/server/location/IContextHubWrapper.java @@ -45,12 +45,7 @@ public abstract class IContextHubWrapper { Log.i(TAG, "Context Hub HAL service not found"); } - ContextHubWrapperV1_0 wrapper = null; - if (proxy != null) { - wrapper = new ContextHubWrapperV1_0(proxy); - } - - return wrapper; + return (proxy == null) ? null : new ContextHubWrapperV1_0(proxy); } /** @@ -69,12 +64,26 @@ public abstract class IContextHubWrapper { Log.i(TAG, "Context Hub HAL service not found"); } - ContextHubWrapperV1_1 wrapper = null; - if (proxy != null) { - wrapper = new ContextHubWrapperV1_1(proxy); + return (proxy == null) ? null : new ContextHubWrapperV1_1(proxy); + } + + /** + * Attempts to connect to the Contexthub HAL 1.2 service, if it exists. + * + * @return A valid IContextHubWrapper if the connection was successful, null otherwise. + */ + @Nullable + public static IContextHubWrapper maybeConnectTo1_2() { + android.hardware.contexthub.V1_2.IContexthub proxy = null; + try { + proxy = android.hardware.contexthub.V1_2.IContexthub.getService(true /* retry */); + } catch (RemoteException e) { + Log.e(TAG, "RemoteException while attaching to Context Hub HAL proxy", e); + } catch (NoSuchElementException e) { + Log.i(TAG, "Context Hub HAL service not found"); } - return wrapper; + return (proxy == null) ? null : new ContextHubWrapperV1_2(proxy); } /** @@ -83,19 +92,29 @@ public abstract class IContextHubWrapper { public abstract android.hardware.contexthub.V1_0.IContexthub getHub(); /** - * @return True if this version of the Contexthub HAL supports setting notifications. + * @return True if this version of the Contexthub HAL supports Location setting notifications. */ - public abstract boolean supportsSettingNotifications(); + public abstract boolean supportsLocationSettingNotifications(); /** - * Notifies the Contexthub implementation of a user setting change. + * Notifies the Contexthub implementation of a user Location setting change. * - * @param setting The user setting that has changed. MUST be one of the values from the - * {@link Setting} enum - * @param newValue The value of the user setting that changed. MUST be one of the values - * from the {@link SettingValue} enum. + * @param enabled True if the Location setting has been enabled. */ - public abstract void onSettingChanged(byte setting, byte newValue); + public abstract void onLocationSettingChanged(boolean enabled); + + /** + * @return True if this version of the Contexthub HAL supports WiFi availability setting + * notifications. + */ + public abstract boolean supportsWifiSettingNotifications(); + + /** + * Notifies the Contexthub implementation of a user WiFi availability setting change. + * + * @param enabled true if the WiFi availability setting has been enabled. + */ + public abstract void onWifiSettingChanged(boolean enabled); private static class ContextHubWrapperV1_0 extends IContextHubWrapper { private android.hardware.contexthub.V1_0.IContexthub mHub; @@ -108,11 +127,19 @@ public abstract class IContextHubWrapper { return mHub; } - public boolean supportsSettingNotifications() { + public boolean supportsLocationSettingNotifications() { return false; } - public void onSettingChanged(byte setting, byte newValue) {} + public boolean supportsWifiSettingNotifications() { + return false; + } + + public void onLocationSettingChanged(boolean enabled) { + } + + public void onWifiSettingChanged(boolean enabled) { + } } private static class ContextHubWrapperV1_1 extends IContextHubWrapper { @@ -126,14 +153,60 @@ public abstract class IContextHubWrapper { return mHub; } - public boolean supportsSettingNotifications() { + public boolean supportsLocationSettingNotifications() { + return true; + } + + public boolean supportsWifiSettingNotifications() { + return false; + } + + public void onLocationSettingChanged(boolean enabled) { + try { + mHub.onSettingChanged(Setting.LOCATION, + enabled ? SettingValue.ENABLED : SettingValue.DISABLED); + } catch (RemoteException e) { + Log.e(TAG, "Failed to send setting change to Contexthub", e); + } + } + + public void onWifiSettingChanged(boolean enabled) { + } + } + + private static class ContextHubWrapperV1_2 extends IContextHubWrapper { + private android.hardware.contexthub.V1_2.IContexthub mHub; + + ContextHubWrapperV1_2(android.hardware.contexthub.V1_2.IContexthub hub) { + mHub = hub; + } + + public android.hardware.contexthub.V1_0.IContexthub getHub() { + return mHub; + } + + public boolean supportsLocationSettingNotifications() { return true; } - public void onSettingChanged(byte setting, byte newValue) { + public boolean supportsWifiSettingNotifications() { + return true; + } + + public void onLocationSettingChanged(boolean enabled) { + sendSettingChanged(Setting.LOCATION, + enabled ? SettingValue.ENABLED : SettingValue.DISABLED); + } + + public void onWifiSettingChanged(boolean enabled) { + sendSettingChanged(android.hardware.contexthub.V1_2.Setting.WIFI_AVAILABLE, + enabled ? SettingValue.ENABLED : SettingValue.DISABLED); + } + + private void sendSettingChanged(byte setting, byte newValue) { try { - mHub.onSettingChanged(setting, newValue); - } catch (RemoteException e) { + mHub.onSettingChanged_1_2(setting, newValue); + } catch (RemoteException e) { Log.e(TAG, "Failed to send setting change to Contexthub", e); } } diff --git a/services/core/java/com/android/server/location/LocationProviderManager.java b/services/core/java/com/android/server/location/LocationProviderManager.java index 52065710f38e..179fb7d2f723 100644 --- a/services/core/java/com/android/server/location/LocationProviderManager.java +++ b/services/core/java/com/android/server/location/LocationProviderManager.java @@ -23,15 +23,12 @@ import static android.location.LocationManager.GPS_PROVIDER; import static android.location.LocationManager.KEY_LOCATION_CHANGED; import static android.location.LocationManager.KEY_PROVIDER_ENABLED; import static android.location.LocationManager.PASSIVE_PROVIDER; -import static android.location.LocationRequest.PASSIVE_INTERVAL; import static android.os.IPowerManager.LOCATION_MODE_NO_CHANGE; import static android.os.PowerManager.LOCATION_MODE_ALL_DISABLED_WHEN_SCREEN_OFF; import static android.os.PowerManager.LOCATION_MODE_FOREGROUND_ONLY; import static android.os.PowerManager.LOCATION_MODE_GPS_DISABLED_WHEN_SCREEN_OFF; import static android.os.PowerManager.LOCATION_MODE_THROTTLE_REQUESTS_WHEN_SCREEN_OFF; -import static com.android.internal.location.ProviderRequest.EMPTY_REQUEST; -import static com.android.internal.location.ProviderRequest.INTERVAL_DISABLED; import static com.android.server.location.LocationManagerService.D; import static com.android.server.location.LocationManagerService.TAG; import static com.android.server.location.LocationPermissions.PERMISSION_COARSE; @@ -503,14 +500,7 @@ class LocationProviderManager extends LocationRequest.Builder builder = new LocationRequest.Builder(baseRequest); if (mPermissionLevel < PERMISSION_FINE) { - switch (baseRequest.getQuality()) { - case LocationRequest.ACCURACY_FINE: - builder.setQuality(LocationRequest.ACCURACY_BLOCK); - break; - case LocationRequest.POWER_HIGH: - builder.setQuality(LocationRequest.POWER_LOW); - break; - } + builder.setQuality(LocationRequest.QUALITY_LOW_POWER); if (baseRequest.getIntervalMillis() < MIN_COARSE_INTERVAL_MS) { builder.setIntervalMillis(MIN_COARSE_INTERVAL_MS); } @@ -1709,7 +1699,7 @@ class LocationProviderManager extends @Override protected boolean registerWithService(ProviderRequest request, Collection<Registration> registrations) { - return reregisterWithService(EMPTY_REQUEST, request, registrations); + return reregisterWithService(ProviderRequest.EMPTY_REQUEST, request, registrations); } @GuardedBy("mLock") @@ -1778,8 +1768,8 @@ class LocationProviderManager extends Preconditions.checkState(Thread.holdsLock(mLock)); } - mLocationEventLog.logProviderUpdateRequest(mName, EMPTY_REQUEST); - mProvider.setRequest(EMPTY_REQUEST); + mLocationEventLog.logProviderUpdateRequest(mName, ProviderRequest.EMPTY_REQUEST); + mProvider.setRequest(ProviderRequest.EMPTY_REQUEST); } @GuardedBy("mLock") @@ -1839,27 +1829,27 @@ class LocationProviderManager extends Preconditions.checkState(Thread.holdsLock(mLock)); } - long intervalMs = INTERVAL_DISABLED; + long intervalMs = ProviderRequest.INTERVAL_DISABLED; + int quality = LocationRequest.QUALITY_LOW_POWER; boolean locationSettingsIgnored = false; boolean lowPower = true; - ArrayList<LocationRequest> locationRequests = new ArrayList<>(registrations.size()); for (Registration registration : registrations) { LocationRequest request = registration.getRequest(); // passive requests do not contribute to the provider request - if (request.getIntervalMillis() == PASSIVE_INTERVAL) { + if (request.getIntervalMillis() == LocationRequest.PASSIVE_INTERVAL) { continue; } intervalMs = min(request.getIntervalMillis(), intervalMs); + quality = min(request.getQuality(), quality); locationSettingsIgnored |= request.isLocationSettingsIgnored(); lowPower &= request.isLowPower(); - locationRequests.add(request); } - if (intervalMs == INTERVAL_DISABLED) { - return EMPTY_REQUEST; + if (intervalMs == ProviderRequest.INTERVAL_DISABLED) { + return ProviderRequest.EMPTY_REQUEST; } // calculate who to blame for power in a somewhat arbitrary fashion. we pick a threshold @@ -1872,7 +1862,7 @@ class LocationProviderManager extends } catch (ArithmeticException e) { // check for and handle overflow by setting to one below the passive interval so passive // requests are automatically skipped - thresholdIntervalMs = PASSIVE_INTERVAL - 1; + thresholdIntervalMs = LocationRequest.PASSIVE_INTERVAL - 1; } WorkSource workSource = new WorkSource(); @@ -1884,9 +1874,9 @@ class LocationProviderManager extends return new ProviderRequest.Builder() .setIntervalMillis(intervalMs) + .setQuality(quality) .setLocationSettingsIgnored(locationSettingsIgnored) .setLowPower(lowPower) - .setLocationRequests(locationRequests) .setWorkSource(workSource) .build(); } diff --git a/services/core/java/com/android/server/location/PassiveLocationProviderManager.java b/services/core/java/com/android/server/location/PassiveLocationProviderManager.java index fc10d5fcf1b7..b7718611cffc 100644 --- a/services/core/java/com/android/server/location/PassiveLocationProviderManager.java +++ b/services/core/java/com/android/server/location/PassiveLocationProviderManager.java @@ -63,15 +63,7 @@ class PassiveLocationProviderManager extends LocationProviderManager { @Override protected ProviderRequest mergeRegistrations(Collection<Registration> registrations) { - boolean locationSettingsIgnored = false; - for (Registration registration : registrations) { - locationSettingsIgnored |= registration.getRequest().isLocationSettingsIgnored(); - } - - return new ProviderRequest.Builder() - .setIntervalMillis(0) - .setLocationSettingsIgnored(locationSettingsIgnored) - .build(); + return new ProviderRequest.Builder().setIntervalMillis(0).build(); } @Override @@ -79,4 +71,9 @@ class PassiveLocationProviderManager extends LocationProviderManager { Collection<Registration> registrations) { return 0; } + + @Override + protected String getServiceState() { + return mProvider.getCurrentRequest().isActive() ? "registered" : "unregistered"; + } } diff --git a/services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java b/services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java index b94f1555cfaa..ec48d4cbcecd 100644 --- a/services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java +++ b/services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java @@ -67,7 +67,7 @@ public abstract class GnssListenerMultiplexer<TRequest, TListener extends IInter /** * Registration object for GNSS listeners. */ - protected final class GnssListenerRegistration extends + protected class GnssListenerRegistration extends BinderListenerRegistration<TRequest, TListener> { // we store these values because we don't trust the listeners not to give us dupes, not to @@ -232,13 +232,21 @@ public abstract class GnssListenerMultiplexer<TRequest, TListener extends IInter final long identity = Binder.clearCallingIdentity(); try { addRegistration(listener.asBinder(), - new GnssListenerRegistration(request, callerIdentity, listener)); + createRegistration(request, callerIdentity, listener)); } finally { Binder.restoreCallingIdentity(identity); } } /** + * May be overridden by subclasses to change the registration type. + */ + protected GnssListenerRegistration createRegistration(TRequest request, + CallerIdentity callerIdentity, TListener listener) { + return new GnssListenerRegistration(request, callerIdentity, listener); + } + + /** * Removes the given listener. */ public void removeListener(TListener listener) { diff --git a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java index e144b403240c..e25e605cf7d2 100644 --- a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java +++ b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java @@ -708,12 +708,12 @@ public class GnssLocationProvider extends AbstractLocationProvider implements // For fast GNSS TTFF provider = LocationManager.NETWORK_PROVIDER; locationListener = mNetworkLocationListener; - locationRequest.setQuality(LocationRequest.POWER_LOW); + locationRequest.setQuality(LocationRequest.QUALITY_LOW_POWER); } else { // For Device-Based Hybrid (E911) provider = LocationManager.FUSED_PROVIDER; locationListener = mFusedLocationListener; - locationRequest.setQuality(LocationRequest.ACCURACY_FINE); + locationRequest.setQuality(LocationRequest.QUALITY_HIGH_ACCURACY); } // Ignore location settings if in emergency mode. This is only allowed for diff --git a/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java b/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java index 2faa15fac42f..74284f357f61 100644 --- a/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java +++ b/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java @@ -19,6 +19,7 @@ package com.android.server.location.gnss; import static com.android.server.location.gnss.GnssManagerService.D; import static com.android.server.location.gnss.GnssManagerService.TAG; +import android.annotation.Nullable; import android.app.AppOpsManager; import android.location.GnssMeasurementsEvent; import android.location.GnssRequest; @@ -32,6 +33,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.Preconditions; import com.android.server.location.util.AppOpsHelper; import com.android.server.location.util.Injector; +import com.android.server.location.util.LocationAttributionHelper; import com.android.server.location.util.LocationUsageLogger; import com.android.server.location.util.SettingsHelper; @@ -44,11 +46,40 @@ import java.util.Objects; * * @hide */ -public class GnssMeasurementsProvider extends +public final class GnssMeasurementsProvider extends GnssListenerMultiplexer<GnssRequest, IGnssMeasurementsListener, Boolean> implements SettingsHelper.GlobalSettingChangedListener { + private class GnssMeasurementListenerRegistration extends GnssListenerRegistration { + + private static final String GNSS_MEASUREMENTS_BUCKET = "gnss_measurement"; + + protected GnssMeasurementListenerRegistration( + @Nullable GnssRequest gnssRequest, + CallerIdentity callerIdentity, + IGnssMeasurementsListener iGnssMeasurementsListener) { + super(gnssRequest, callerIdentity, iGnssMeasurementsListener); + } + + @Nullable + @Override + protected ListenerOperation<IGnssMeasurementsListener> onActive() { + mLocationAttributionHelper.reportHighPowerLocationStart( + getIdentity(), GNSS_MEASUREMENTS_BUCKET, getKey()); + return null; + } + + @Nullable + @Override + protected ListenerOperation<IGnssMeasurementsListener> onInactive() { + mLocationAttributionHelper.reportHighPowerLocationStop( + getIdentity(), GNSS_MEASUREMENTS_BUCKET, getKey()); + return null; + } + } + private final AppOpsHelper mAppOpsHelper; + private final LocationAttributionHelper mLocationAttributionHelper; private final LocationUsageLogger mLogger; private final GnssMeasurementProviderNative mNative; @@ -60,6 +91,7 @@ public class GnssMeasurementsProvider extends public GnssMeasurementsProvider(Injector injector, GnssMeasurementProviderNative aNative) { super(injector); mAppOpsHelper = injector.getAppOpsHelper(); + mLocationAttributionHelper = injector.getLocationAttributionHelper(); mLogger = injector.getLocationUsageLogger(); mNative = aNative; } @@ -76,6 +108,12 @@ public class GnssMeasurementsProvider extends } @Override + protected GnssListenerRegistration createRegistration(GnssRequest request, + CallerIdentity callerIdentity, IGnssMeasurementsListener listener) { + return new GnssMeasurementListenerRegistration(request, callerIdentity, listener); + } + + @Override protected boolean registerWithService(Boolean fullTrackingRequest, Collection<GnssListenerRegistration> registrations) { Preconditions.checkState(mNative.isMeasurementSupported()); diff --git a/services/core/java/com/android/server/location/util/LocationAttributionHelper.java b/services/core/java/com/android/server/location/util/LocationAttributionHelper.java index bc3ac0ff2e48..36b3ef31ef48 100644 --- a/services/core/java/com/android/server/location/util/LocationAttributionHelper.java +++ b/services/core/java/com/android/server/location/util/LocationAttributionHelper.java @@ -38,12 +38,12 @@ import java.util.Set; */ public class LocationAttributionHelper { - private static class ProviderListener { - private final String mProvider; + private static class BucketKey { + private final String mBucket; private final Object mKey; - private ProviderListener(String provider, Object key) { - mProvider = Objects.requireNonNull(provider); + private BucketKey(String bucket, Object key) { + mBucket = Objects.requireNonNull(bucket); mKey = Objects.requireNonNull(key); } @@ -56,23 +56,23 @@ public class LocationAttributionHelper { return false; } - ProviderListener that = (ProviderListener) o; - return mProvider.equals(that.mProvider) + BucketKey that = (BucketKey) o; + return mBucket.equals(that.mBucket) && mKey.equals(that.mKey); } @Override public int hashCode() { - return Objects.hash(mProvider, mKey); + return Objects.hash(mBucket, mKey); } } private final AppOpsHelper mAppOpsHelper; @GuardedBy("this") - private final Map<CallerIdentity, Set<ProviderListener>> mAttributions; + private final Map<CallerIdentity, Set<BucketKey>> mAttributions; @GuardedBy("this") - private final Map<CallerIdentity, Set<ProviderListener>> mHighPowerAttributions; + private final Map<CallerIdentity, Set<BucketKey>> mHighPowerAttributions; public LocationAttributionHelper(AppOpsHelper appOpsHelper) { mAppOpsHelper = appOpsHelper; @@ -82,14 +82,14 @@ public class LocationAttributionHelper { } /** - * Report normal location usage for the given caller on the given provider, with a unique key. + * Report normal location usage for the given caller in the given bucket, with a unique key. */ - public synchronized void reportLocationStart(CallerIdentity identity, String provider, + public synchronized void reportLocationStart(CallerIdentity identity, String bucket, Object key) { - Set<ProviderListener> keySet = mAttributions.computeIfAbsent(identity, + Set<BucketKey> keySet = mAttributions.computeIfAbsent(identity, i -> new ArraySet<>()); boolean empty = keySet.isEmpty(); - if (keySet.add(new ProviderListener(provider, key)) && empty) { + if (keySet.add(new BucketKey(bucket, key)) && empty) { if (!mAppOpsHelper.startOpNoThrow(OP_MONITOR_LOCATION, identity)) { mAttributions.remove(identity); } @@ -97,13 +97,13 @@ public class LocationAttributionHelper { } /** - * Report normal location usage has stopped for the given caller on the given provider, with a + * Report normal location usage has stopped for the given caller in the given bucket, with a * unique key. */ - public synchronized void reportLocationStop(CallerIdentity identity, String provider, + public synchronized void reportLocationStop(CallerIdentity identity, String bucket, Object key) { - Set<ProviderListener> keySet = mAttributions.get(identity); - if (keySet != null && keySet.remove(new ProviderListener(provider, key)) + Set<BucketKey> keySet = mAttributions.get(identity); + if (keySet != null && keySet.remove(new BucketKey(bucket, key)) && keySet.isEmpty()) { mAttributions.remove(identity); mAppOpsHelper.finishOp(OP_MONITOR_LOCATION, identity); @@ -111,15 +111,15 @@ public class LocationAttributionHelper { } /** - * Report high power location usage for the given caller on the given provider, with a unique + * Report high power location usage for the given caller in the given bucket, with a unique * key. */ - public synchronized void reportHighPowerLocationStart(CallerIdentity identity, String provider, + public synchronized void reportHighPowerLocationStart(CallerIdentity identity, String bucket, Object key) { - Set<ProviderListener> keySet = mHighPowerAttributions.computeIfAbsent(identity, + Set<BucketKey> keySet = mHighPowerAttributions.computeIfAbsent(identity, i -> new ArraySet<>()); boolean empty = keySet.isEmpty(); - if (keySet.add(new ProviderListener(provider, key)) && empty) { + if (keySet.add(new BucketKey(bucket, key)) && empty) { if (mAppOpsHelper.startOpNoThrow(OP_MONITOR_HIGH_POWER_LOCATION, identity)) { if (D) { Log.v(TAG, "starting high power location attribution for " + identity); @@ -131,13 +131,13 @@ public class LocationAttributionHelper { } /** - * Report high power location usage has stopped for the given caller on the given provider, + * Report high power location usage has stopped for the given caller in the given bucket, * with a unique key. */ - public synchronized void reportHighPowerLocationStop(CallerIdentity identity, String provider, + public synchronized void reportHighPowerLocationStop(CallerIdentity identity, String bucket, Object key) { - Set<ProviderListener> keySet = mHighPowerAttributions.get(identity); - if (keySet != null && keySet.remove(new ProviderListener(provider, key)) + Set<BucketKey> keySet = mHighPowerAttributions.get(identity); + if (keySet != null && keySet.remove(new BucketKey(bucket, key)) && keySet.isEmpty()) { if (D) { Log.v(TAG, "stopping high power location attribution for " + identity); diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java index 793cfcd77414..f9973529a120 100644 --- a/services/core/java/com/android/server/media/MediaSessionRecord.java +++ b/services/core/java/com/android/server/media/MediaSessionRecord.java @@ -382,7 +382,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient, MediaSessionR if (mPlaybackState == null) { return false; } - return MediaSession.isActiveState(mPlaybackState.getState()) == expected; + return mPlaybackState.isActiveState() == expected; } /** diff --git a/services/core/java/com/android/server/net/NetworkStatsAccess.java b/services/core/java/com/android/server/net/NetworkStatsAccess.java index 5a8fbf3d26b5..7cdc4cc7479d 100644 --- a/services/core/java/com/android/server/net/NetworkStatsAccess.java +++ b/services/core/java/com/android/server/net/NetworkStatsAccess.java @@ -27,6 +27,7 @@ import android.app.AppOpsManager; import android.app.admin.DevicePolicyManagerInternal; import android.content.Context; import android.content.pm.PackageManager; +import android.os.Process; import android.os.UserHandle; import android.telephony.TelephonyManager; @@ -110,11 +111,12 @@ public final class NetworkStatsAccess { boolean hasCarrierPrivileges = tm != null && tm.checkCarrierPrivilegesForPackageAnyPhone(callingPackage) == TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS; - boolean isDeviceOwner = dpmi != null && dpmi.isActiveDeviceOwner(callingUid); + final boolean isDeviceOwner = dpmi != null && dpmi.isActiveDeviceOwner(callingUid); + final int appId = UserHandle.getAppId(callingUid); if (hasCarrierPrivileges || isDeviceOwner - || UserHandle.getAppId(callingUid) == android.os.Process.SYSTEM_UID) { - // Carrier-privileged apps and device owners, and the system can access data usage for - // all apps on the device. + || appId == Process.SYSTEM_UID || appId == Process.NETWORK_STACK_UID) { + // Carrier-privileged apps and device owners, and the system (including the + // network stack) can access data usage for all apps on the device. return NetworkStatsAccess.Level.DEVICE; } diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 23798c0622fc..4d6b760fc56f 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -4769,7 +4769,7 @@ public class NotificationManagerService extends SystemService { @Override public List<ComponentName> getEnabledNotificationListeners(int userId) { - checkCallerIsSystem(); + checkNotificationListenerAccess(); return mListeners.getAllowedComponents(userId); } @@ -4838,7 +4838,7 @@ public class NotificationManagerService extends SystemService { public void setNotificationListenerAccessGrantedForUser(ComponentName listener, int userId, boolean granted) { Objects.requireNonNull(listener); - checkCallerIsSystemOrShell(); + checkNotificationListenerAccess(); final long identity = Binder.clearCallingIdentity(); try { if (mAllowedManagedServicePackages.test( @@ -5111,6 +5111,14 @@ public class NotificationManagerService extends SystemService { } }; + protected void checkNotificationListenerAccess() { + if (!isCallerSystemOrPhone()) { + getContext().enforceCallingPermission( + permission.MANAGE_NOTIFICATION_LISTENERS, + "Caller must hold " + permission.MANAGE_NOTIFICATION_LISTENERS); + } + } + @VisibleForTesting protected void setNotificationAssistantAccessGrantedForUserInternal( ComponentName assistant, int baseUserId, boolean granted) { @@ -6644,7 +6652,7 @@ public class NotificationManagerService extends SystemService { // Log event to statsd mNotificationRecordLogger.maybeLogNotificationPosted(r, old, position, - buzzBeepBlinkLoggingCode, getGroupInstanceId(n.getGroupKey())); + buzzBeepBlinkLoggingCode, getGroupInstanceId(r.getSbn().getGroupKey())); } finally { int N = mEnqueuedNotifications.size(); for (int i = 0; i < N; i++) { diff --git a/services/core/java/com/android/server/om/IdmapManager.java b/services/core/java/com/android/server/om/IdmapManager.java index cb6e960b721d..eeb26552e63d 100644 --- a/services/core/java/com/android/server/om/IdmapManager.java +++ b/services/core/java/com/android/server/om/IdmapManager.java @@ -57,7 +57,7 @@ final class IdmapManager { private final PackageManagerHelper mPackageManager; /** - * Package name of the reference package defined in 'config-signature' tag of + * Package name of the reference package defined in 'overlay-config-signature' tag of * SystemConfig or empty String if tag not defined. This package is vetted on scan by * PackageManagerService that it's a system package and is used to check if overlay matches * its signature in order to fulfill the config_signature policy. @@ -159,7 +159,7 @@ final class IdmapManager { fulfilledPolicies |= OverlayablePolicy.ACTOR_SIGNATURE; } - // If SystemConfig defines 'config-signature' package, given that + // If SystemConfig defines 'overlay-config-signature' package, given that // this package is vetted by OverlayManagerService that it's a // preinstalled package, check if overlay matches its signature. if (!TextUtils.isEmpty(mConfigSignaturePackage) diff --git a/services/core/java/com/android/server/pm/ApexManager.java b/services/core/java/com/android/server/pm/ApexManager.java index ad022c764547..7992fea4a675 100644 --- a/services/core/java/com/android/server/pm/ApexManager.java +++ b/services/core/java/com/android/server/pm/ApexManager.java @@ -777,12 +777,15 @@ public abstract class ApexManager { void registerApkInApex(AndroidPackage pkg) { synchronized (mLock) { for (ActiveApexInfo aai : mActiveApexInfosCache) { - if (pkg.getBaseApkPath().startsWith(aai.apexDirectory.getAbsolutePath())) { + if (pkg.getBaseApkPath().startsWith( + aai.apexDirectory.getAbsolutePath() + File.separator)) { List<String> apks = mApksInApex.get(aai.apexModuleName); if (apks == null) { apks = Lists.newArrayList(); mApksInApex.put(aai.apexModuleName, apks); } + Slog.i(TAG, "Registering " + pkg.getPackageName() + " as apk-in-apex of " + + aai.apexModuleName); apks.add(pkg.getPackageName()); } } diff --git a/services/core/java/com/android/server/pm/IncrementalStates.java b/services/core/java/com/android/server/pm/IncrementalStates.java index dda5fafb8cb1..43f4a3477a98 100644 --- a/services/core/java/com/android/server/pm/IncrementalStates.java +++ b/services/core/java/com/android/server/pm/IncrementalStates.java @@ -16,18 +16,21 @@ package com.android.server.pm; -import android.content.pm.IDataLoaderStatusListener; +import static android.os.incremental.IStorageHealthListener.HEALTH_STATUS_OK; +import static android.os.incremental.IStorageHealthListener.HEALTH_STATUS_UNHEALTHY; +import static android.os.incremental.IStorageHealthListener.HEALTH_STATUS_UNHEALTHY_STORAGE; +import static android.os.incremental.IStorageHealthListener.HEALTH_STATUS_UNHEALTHY_TRANSPORT; + import android.content.pm.IncrementalStatesInfo; import android.content.pm.PackageManager; import android.os.Handler; -import android.os.incremental.IStorageHealthListener; import android.util.Slog; import com.android.internal.annotations.GuardedBy; import com.android.internal.os.BackgroundThread; import com.android.internal.util.function.pooled.PooledLambda; -import java.util.function.BiConsumer; +import java.util.function.Consumer; /** * Manages state transitions of a package installed on Incremental File System. Currently manages: @@ -36,8 +39,7 @@ import java.util.function.BiConsumer; * * The following events might change the states of a package: * 1. Installation commit - * 2. Incremental storage health - * 3. Data loader stream health + * 2. Incremental storage health changes * 4. Loading progress changes * * @hide @@ -48,16 +50,14 @@ public final class IncrementalStates { private final Handler mHandler = BackgroundThread.getHandler(); private final Object mLock = new Object(); @GuardedBy("mLock") - private int mStreamStatus = IDataLoaderStatusListener.STREAM_HEALTHY; - @GuardedBy("mLock") - private int mStorageHealthStatus = IStorageHealthListener.HEALTH_STATUS_OK; + private int mStorageHealthStatus = HEALTH_STATUS_OK; @GuardedBy("mLock") private final LoadingState mLoadingState; @GuardedBy("mLock") private StartableState mStartableState; @GuardedBy("mLock") private Callback mCallback = null; - private final BiConsumer<Integer, Integer> mStatusConsumer; + private final Consumer<Integer> mStatusConsumer; public IncrementalStates() { // By default the package is not startable and not fully loaded (i.e., is loading) @@ -148,12 +148,9 @@ public final class IncrementalStates { } } - private class StatusConsumer implements BiConsumer<Integer, Integer> { + private class StatusConsumer implements Consumer<Integer> { @Override - public void accept(Integer streamStatus, Integer storageStatus) { - if (streamStatus == null && storageStatus == null) { - return; - } + public void accept(Integer storageStatus) { final boolean oldState, newState; synchronized (mLock) { if (!mLoadingState.isLoading()) { @@ -161,12 +158,7 @@ public final class IncrementalStates { return; } oldState = mStartableState.isStartable(); - if (streamStatus != null) { - mStreamStatus = (Integer) streamStatus; - } - if (storageStatus != null) { - mStorageHealthStatus = (Integer) storageStatus; - } + mStorageHealthStatus = storageStatus; updateStartableStateLocked(); newState = mStartableState.isStartable(); } @@ -188,21 +180,7 @@ public final class IncrementalStates { Slog.i(TAG, "received storage health status changed event : storageHealthStatus=" + storageHealthStatus); } - mStatusConsumer.accept(null, storageHealthStatus); - } - - /** - * By calling this method, the caller indicates that the stream status of the package has - * been - * changed. This could indicate a streaming error. The state will change according to the - * status - * code defined in {@code IDataLoaderStatusListener}. - */ - public void onStreamStatusChanged(int streamState) { - if (DEBUG) { - Slog.i(TAG, "received stream status changed event : streamState=" + streamState); - } - mStatusConsumer.accept(streamState, null); + mStatusConsumer.accept(storageHealthStatus); } /** @@ -284,35 +262,16 @@ public final class IncrementalStates { final boolean currentState = mStartableState.isStartable(); boolean nextState = currentState; if (!currentState) { - if (mStorageHealthStatus == IStorageHealthListener.HEALTH_STATUS_OK - && mStreamStatus == IDataLoaderStatusListener.STREAM_HEALTHY) { - // change from unstartable -> startable when both stream and storage are healthy + if (mStorageHealthStatus == HEALTH_STATUS_OK) { + // change from unstartable -> startable nextState = true; } } else { - if (mStorageHealthStatus == IStorageHealthListener.HEALTH_STATUS_UNHEALTHY) { - // unrecoverable if storage is unhealthy + if (mStorageHealthStatus == HEALTH_STATUS_UNHEALTHY + || mStorageHealthStatus == HEALTH_STATUS_UNHEALTHY_STORAGE + || mStorageHealthStatus == HEALTH_STATUS_UNHEALTHY_TRANSPORT) { + // change from startable -> unstartable nextState = false; - } else { - switch (mStreamStatus) { - case IDataLoaderStatusListener.STREAM_INTEGRITY_ERROR: - // unrecoverable, fall through - case IDataLoaderStatusListener.STREAM_SOURCE_ERROR: { - // unrecoverable - nextState = false; - break; - } - case IDataLoaderStatusListener.STREAM_STORAGE_ERROR: { - if (mStorageHealthStatus != IStorageHealthListener.HEALTH_STATUS_OK) { - // unrecoverable if there is a pending read AND storage is limited - nextState = false; - } - break; - } - default: - // anything else, remain startable - break; - } } } if (nextState == currentState) { @@ -370,17 +329,11 @@ public final class IncrementalStates { return PackageManager.UNSTARTABLE_REASON_UNKNOWN; } // Translate stream status to reason for unstartable state - switch (mStreamStatus) { - case IDataLoaderStatusListener.STREAM_TRANSPORT_ERROR: - // fall through - case IDataLoaderStatusListener.STREAM_INTEGRITY_ERROR: - // fall through - case IDataLoaderStatusListener.STREAM_SOURCE_ERROR: { - return PackageManager.UNSTARTABLE_REASON_DATALOADER_TRANSPORT; - } - case IDataLoaderStatusListener.STREAM_STORAGE_ERROR: { - return PackageManager.UNSTARTABLE_REASON_DATALOADER_STORAGE; - } + switch (mStorageHealthStatus) { + case HEALTH_STATUS_UNHEALTHY_STORAGE: + return PackageManager.UNSTARTABLE_REASON_INSUFFICIENT_STORAGE; + case HEALTH_STATUS_UNHEALTHY_TRANSPORT: + return PackageManager.UNSTARTABLE_REASON_CONNECTION_ERROR; default: return PackageManager.UNSTARTABLE_REASON_UNKNOWN; } @@ -464,7 +417,6 @@ public final class IncrementalStates { } IncrementalStates l = (IncrementalStates) o; return l.mStorageHealthStatus == mStorageHealthStatus - && l.mStreamStatus == mStreamStatus && l.mStartableState.equals(mStartableState) && l.mLoadingState.equals(mLoadingState); } @@ -474,7 +426,6 @@ public final class IncrementalStates { int hashCode = mStartableState.hashCode(); hashCode = 31 * hashCode + mLoadingState.hashCode(); hashCode = 31 * hashCode + mStorageHealthStatus; - hashCode = 31 * hashCode + mStreamStatus; return hashCode; } } diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java index 649cafb1cb06..5d2928e1a854 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerSession.java +++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java @@ -143,7 +143,6 @@ import com.android.internal.util.ArrayUtils; import com.android.internal.util.FrameworkStatsLog; import com.android.internal.util.IndentingPrintWriter; import com.android.internal.util.Preconditions; -import com.android.internal.util.function.pooled.PooledLambda; import com.android.server.LocalServices; import com.android.server.pm.Installer.InstallerException; import com.android.server.pm.dex.DexManager; @@ -1702,28 +1701,6 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { dispatchSessionFinished(error, detailedMessage, null); } - private void onStorageHealthStatusChanged(int status) { - final String packageName = getPackageName(); - if (TextUtils.isEmpty(packageName)) { - // The package has not been installed. - return; - } - mHandler.post(PooledLambda.obtainRunnable( - PackageManagerService::onStorageHealthStatusChanged, - mPm, packageName, status, userId).recycleOnUse()); - } - - private void onStreamHealthStatusChanged(int status) { - final String packageName = getPackageName(); - if (TextUtils.isEmpty(packageName)) { - // The package has not been installed. - return; - } - mHandler.post(PooledLambda.obtainRunnable( - PackageManagerService::onStreamStatusChanged, - mPm, packageName, status, userId).recycleOnUse()); - } - /** * If session should be sealed, then it's sealed to prevent further modification. * If the session can't be sealed then it's destroyed. @@ -3315,19 +3292,11 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { return; } - final boolean isDestroyedOrDataLoaderFinished; synchronized (mLock) { - isDestroyedOrDataLoaderFinished = mDestroyed || mDataLoaderFinished; - } - if (isDestroyedOrDataLoaderFinished) { - switch (status) { - case IDataLoaderStatusListener.DATA_LOADER_UNRECOVERABLE: - // treat as unhealthy storage - onStorageHealthStatusChanged( - IStorageHealthListener.HEALTH_STATUS_UNHEALTHY); - return; + if (mDestroyed || mDataLoaderFinished) { + // No need to worry about post installation + return; } - return; } try { @@ -3423,13 +3392,10 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } @Override public void reportStreamHealth(int dataLoaderId, int streamStatus) { - synchronized (mLock) { - if (!mDestroyed && !mDataLoaderFinished) { - // ignore streaming status if package isn't installed - return; - } - } - onStreamHealthStatusChanged(streamStatus); + // Currently the stream status is not used during package installation. It is + // technically possible for the data loader to report stream status via this + // callback, but if something is wrong with the streaming, it is more likely that + // prepareDataLoaderLocked will return false and the installation will be aborted. } }; @@ -3438,20 +3404,16 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { healthCheckParams.blockedTimeoutMs = INCREMENTAL_STORAGE_BLOCKED_TIMEOUT_MS; healthCheckParams.unhealthyTimeoutMs = INCREMENTAL_STORAGE_UNHEALTHY_TIMEOUT_MS; healthCheckParams.unhealthyMonitoringMs = INCREMENTAL_STORAGE_UNHEALTHY_MONITORING_MS; - final boolean systemDataLoader = params.getComponentName().getPackageName() == SYSTEM_DATA_LOADER_PACKAGE; final IStorageHealthListener healthListener = new IStorageHealthListener.Stub() { @Override public void onHealthStatus(int storageId, int status) { - final boolean isDestroyedOrDataLoaderFinished; synchronized (mLock) { - isDestroyedOrDataLoaderFinished = mDestroyed || mDataLoaderFinished; - } - if (isDestroyedOrDataLoaderFinished) { - // App's installed. - onStorageHealthStatusChanged(status); - return; + if (mDestroyed || mDataLoaderFinished) { + // No need to worry about post installation + return; + } } switch (status) { diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 805fbbc75a87..3f16abfa04b7 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -271,8 +271,10 @@ import android.os.Trace; import android.os.UserHandle; import android.os.UserManager; import android.os.UserManagerInternal; +import android.os.incremental.IStorageHealthListener; import android.os.incremental.IncrementalManager; import android.os.incremental.IncrementalStorage; +import android.os.incremental.StorageHealthCheckParams; import android.os.storage.DiskInfo; import android.os.storage.IStorageManager; import android.os.storage.StorageEventListener; @@ -506,9 +508,6 @@ public class PackageManagerService extends IPackageManager.Stub private static final boolean HIDE_EPHEMERAL_APIS = false; - private static final boolean ENABLE_FREE_CACHE_V2 = - SystemProperties.getBoolean("fw.free_cache_v2", true); - private static final String PRECOMPILE_LAYOUTS = "pm.precompile_layouts"; private static final int RADIO_UID = Process.PHONE_UID; @@ -732,14 +731,23 @@ public class PackageManagerService extends IPackageManager.Stub private static final String RANDOM_DIR_PREFIX = "~~"; + /** + * Timeout configurations for incremental storage health monitor. + * See {@link IStorageHealthListener} + */ + private static final int INCREMENTAL_STORAGE_BLOCKED_TIMEOUT_MS = 2000; + private static final int INCREMENTAL_STORAGE_UNHEALTHY_TIMEOUT_MS = 7000; + private static final int INCREMENTAL_STORAGE_UNHEALTHY_MONITORING_MS = 60000; + final ServiceThread mHandlerThread; final Handler mHandler; private final ProcessLoggingHandler mProcessLoggingHandler; - final int mSdkVersion = Build.VERSION.SDK_INT; + private final boolean mEnableFreeCacheV2; + final int mSdkVersion; final Context mContext; final boolean mFactoryTest; final boolean mOnlyCore; @@ -894,24 +902,8 @@ public class PackageManagerService extends IPackageManager.Stub T produce(Injector injector, PackageManagerService packageManager); } - static class LocalServicesProducer<T> implements Producer<T> { - private final Class<T> mProducingClass; - LocalServicesProducer(Class<T> clazz) { - this.mProducingClass = clazz; - } - public T produce(Injector injector, PackageManagerService packageManager) { - return LocalServices.getService(mProducingClass); - } - } - - static class SystemServiceProducer<T> implements Producer<T> { - private final Class<T> mProducingClass; - SystemServiceProducer(Class<T> clazz) { - this.mProducingClass = clazz; - } - public T produce(Injector injector, PackageManagerService packageManager) { - return packageManager.mContext.getSystemService(mProducingClass); - } + interface ServiceProducer { + <T> T produce(Class<T> c); } @VisibleForTesting(visibility = Visibility.PRIVATE) @@ -941,41 +933,43 @@ public class PackageManagerService extends IPackageManager.Stub // ----- producers ----- private final Singleton<ComponentResolver> mComponentResolverProducer; - private final Singleton<PermissionManagerServiceInternal> mPermissionManagerProducer; + private final Singleton<PermissionManagerServiceInternal> mPermissionManagerServiceProducer; private final Singleton<UserManagerService> mUserManagerProducer; private final Singleton<Settings> mSettingsProducer; - private final Singleton<ActivityTaskManagerInternal> mActivityTaskManagerProducer; - private final Singleton<ActivityManagerInternal> mActivityManagerInternalProducer; - private final Singleton<DeviceIdleInternal> mLocalDeviceIdleController; - private final Singleton<StorageManagerInternal> mStorageManagerInternalProducer; - private final Singleton<NetworkPolicyManagerInternal> mNetworkPolicyManagerProducer; - private final Singleton<PermissionPolicyInternal> mPermissionPolicyProducer; - private final Singleton<DeviceStorageMonitorInternal> mDeviceStorageMonitorProducer; - private final Singleton<DisplayManager> mDisplayManagerProducer; - private final Singleton<StorageManager> mStorageManagerProducer; - private final Singleton<AppOpsManager> mAppOpsManagerProducer; private final Singleton<AppsFilter> mAppsFilterProducer; private final Singleton<PlatformCompat> mPlatformCompatProducer; + private final Singleton<SystemConfig> mSystemConfigProducer; + private final Singleton<PackageDexOptimizer> mPackageDexOptimizerProducer; + private final Singleton<DexManager> mDexManagerProducer; + private final Singleton<ArtManagerService> mArtManagerServiceProducer; + private final Singleton<ApexManager> mApexManagerProducer; + private final Singleton<ViewCompiler> mViewCompilerProducer; + private final Singleton<IPermissionManager> mPermissionManagerProducer; + private final Singleton<IncrementalManager> mIncrementalManagerProducer; + private final SystemWrapper mSystemWrapper; + private final ServiceProducer mGetLocalServiceProducer; + private final ServiceProducer mGetSystemServiceProducer; Injector(Context context, Object lock, Installer installer, Object installLock, PackageAbiHelper abiHelper, Handler backgroundHandler, Producer<ComponentResolver> componentResolverProducer, - Producer<PermissionManagerServiceInternal> permissionManagerProducer, + Producer<PermissionManagerServiceInternal> permissionManagerServiceProducer, Producer<UserManagerService> userManagerProducer, Producer<Settings> settingsProducer, - Producer<ActivityTaskManagerInternal> activityTaskManagerProducer, - Producer<ActivityManagerInternal> activityManagerInternalProducer, - Producer<DeviceIdleInternal> deviceIdleControllerProducer, - Producer<StorageManagerInternal> storageManagerInternalProducer, - Producer<NetworkPolicyManagerInternal> networkPolicyManagerProducer, - Producer<PermissionPolicyInternal> permissionPolicyProvider, - Producer<DeviceStorageMonitorInternal> deviceStorageMonitorProducer, - Producer<DisplayManager> displayManagerProducer, - Producer<StorageManager> storageManagerProducer, - Producer<AppOpsManager> appOpsManagerProducer, Producer<AppsFilter> appsFilterProducer, - Producer<PlatformCompat> platformCompatProducer) { + Producer<PlatformCompat> platformCompatProducer, + Producer<SystemConfig> systemConfigProducer, + Producer<PackageDexOptimizer> packageDexOptimizerProducer, + Producer<DexManager> dexManagerProducer, + Producer<ArtManagerService> artManagerServiceProducer, + Producer<ApexManager> apexManagerProducer, + Producer<IPermissionManager> permissionManagerProducer, + Producer<ViewCompiler> viewCompilerProducer, + Producer<IncrementalManager> incrementalManagerProducer, + SystemWrapper systemWrapper, + ServiceProducer getLocalServiceProducer, + ServiceProducer getSystemServiceProducer) { mContext = context; mLock = lock; mInstaller = installer; @@ -984,21 +978,22 @@ public class PackageManagerService extends IPackageManager.Stub mBackgroundHandler = backgroundHandler; mBackgroundExecutor = new HandlerExecutor(backgroundHandler); mComponentResolverProducer = new Singleton<>(componentResolverProducer); - mPermissionManagerProducer = new Singleton<>(permissionManagerProducer); + mPermissionManagerServiceProducer = new Singleton<>(permissionManagerServiceProducer); mUserManagerProducer = new Singleton<>(userManagerProducer); mSettingsProducer = new Singleton<>(settingsProducer); - mActivityTaskManagerProducer = new Singleton<>(activityTaskManagerProducer); - mActivityManagerInternalProducer = new Singleton<>(activityManagerInternalProducer); - mLocalDeviceIdleController = new Singleton<>(deviceIdleControllerProducer); - mStorageManagerInternalProducer = new Singleton<>(storageManagerInternalProducer); - mNetworkPolicyManagerProducer = new Singleton<>(networkPolicyManagerProducer); - mPermissionPolicyProducer = new Singleton<>(permissionPolicyProvider); - mDeviceStorageMonitorProducer = new Singleton<>(deviceStorageMonitorProducer); - mDisplayManagerProducer = new Singleton<>(displayManagerProducer); - mStorageManagerProducer = new Singleton<>(storageManagerProducer); - mAppOpsManagerProducer = new Singleton<>(appOpsManagerProducer); mAppsFilterProducer = new Singleton<>(appsFilterProducer); mPlatformCompatProducer = new Singleton<>(platformCompatProducer); + mSystemConfigProducer = new Singleton<>(systemConfigProducer); + mPackageDexOptimizerProducer = new Singleton<>(packageDexOptimizerProducer); + mDexManagerProducer = new Singleton<>(dexManagerProducer); + mArtManagerServiceProducer = new Singleton<>(artManagerServiceProducer); + mApexManagerProducer = new Singleton<>(apexManagerProducer); + mPermissionManagerProducer = new Singleton<>(permissionManagerProducer); + mViewCompilerProducer = new Singleton<>(viewCompilerProducer); + mIncrementalManagerProducer = new Singleton<>(incrementalManagerProducer); + mSystemWrapper = systemWrapper; + mGetLocalServiceProducer = getLocalServiceProducer; + mGetSystemServiceProducer = getSystemServiceProducer; } /** @@ -1038,7 +1033,7 @@ public class PackageManagerService extends IPackageManager.Stub } public PermissionManagerServiceInternal getPermissionManagerServiceInternal() { - return mPermissionManagerProducer.get(this, mPackageManager); + return mPermissionManagerServiceProducer.get(this, mPackageManager); } public Context getContext() { @@ -1049,60 +1044,112 @@ public class PackageManagerService extends IPackageManager.Stub return mSettingsProducer.get(this, mPackageManager); } - public ActivityTaskManagerInternal getActivityTaskManagerInternal() { - return mActivityTaskManagerProducer.get(this, mPackageManager); + public AppsFilter getAppsFilter() { + return mAppsFilterProducer.get(this, mPackageManager); } - public ActivityManagerInternal getActivityManagerInternal() { - return mActivityManagerInternalProducer.get(this, mPackageManager); + public PlatformCompat getCompatibility() { + return mPlatformCompatProducer.get(this, mPackageManager); } - public DeviceIdleInternal getLocalDeviceIdleController() { - return mLocalDeviceIdleController.get(this, mPackageManager); + public SystemConfig getSystemConfig() { + return mSystemConfigProducer.get(this, mPackageManager); } - public StorageManagerInternal getStorageManagerInternal() { - return mStorageManagerInternalProducer.get(this, mPackageManager); + public PackageDexOptimizer getPackageDexOptimizer() { + return mPackageDexOptimizerProducer.get(this, mPackageManager); } - public NetworkPolicyManagerInternal getNetworkPolicyManagerInternal() { - return mNetworkPolicyManagerProducer.get(this, mPackageManager); + public DexManager getDexManager() { + return mDexManagerProducer.get(this, mPackageManager); } - public PermissionPolicyInternal getPermissionPolicyInternal() { - return mPermissionPolicyProducer.get(this, mPackageManager); + public ArtManagerService getArtManagerService() { + return mArtManagerServiceProducer.get(this, mPackageManager); } - public DeviceStorageMonitorInternal getDeviceStorageMonitorInternal() { - return mDeviceStorageMonitorProducer.get(this, mPackageManager); + public ApexManager getApexManager() { + return mApexManagerProducer.get(this, mPackageManager); } - public DisplayManager getDisplayManager() { - return mDisplayManagerProducer.get(this, mPackageManager); + public ViewCompiler getViewCompiler() { + return mViewCompilerProducer.get(this, mPackageManager); } - public StorageManager getStorageManager() { - return mStorageManagerProducer.get(this, mPackageManager); + public IPermissionManager getPermissionManagerService() { + return mPermissionManagerProducer.get(this, mPackageManager); } - public AppOpsManager getAppOpsManager() { - return mAppOpsManagerProducer.get(this, mPackageManager); + public Handler getBackgroundHandler() { + return mBackgroundHandler; } - public AppsFilter getAppsFilter() { - return mAppsFilterProducer.get(this, mPackageManager); + public Executor getBackgroundExecutor() { + return mBackgroundExecutor; } - public PlatformCompat getCompatibility() { - return mPlatformCompatProducer.get(this, mPackageManager); + public <T> T getLocalService(Class<T> c) { + return mGetLocalServiceProducer.produce(c); } - public Handler getBackgroundHandler() { - return mBackgroundHandler; + public <T> T getSystemService(Class<T> c) { + return mGetSystemServiceProducer.produce(c); } - public Executor getBackgroundExecutor() { - return mBackgroundExecutor; + public SystemWrapper getSystemWrapper() { + return mSystemWrapper; + } + + public IncrementalManager getIncrementalManager() { + return mIncrementalManagerProducer.get(this, mPackageManager); + } + } + + /** Provides an abstraction to static access to system state. */ + public interface SystemWrapper { + /** @see SystemProperties#get(String) */ + String getProperty(String key); + /** @see SystemProperties#getInt(String, int) */ + int getPropertyInt(String key, int defValue); + /** @see SystemProperties#getBoolean(String, boolean) */ + boolean getPropertyBoolean(String key, boolean defValue); + /** @see SystemProperties#digestOf(String...) */ + String digestOfProperties(@NonNull String... keys); + /** @see SystemProperties#set(String, String) */ + void setProperty(String key, String value); + /** @see Build.VERSION#SDK_INT */ + int getSdkInt(); + } + + private static class DefaultSystemWrapper implements SystemWrapper { + @Override + public String getProperty(String key) { + return SystemProperties.get(key); + } + + @Override + public int getPropertyInt(String key, int defValue) { + return SystemProperties.getInt(key, defValue); + } + + @Override + public boolean getPropertyBoolean(String key, boolean defValue) { + return SystemProperties.getBoolean(key, defValue); + } + + @Override + public String digestOfProperties(String... keys) { + return SystemProperties.digestOf(keys); + } + + @Override + public void setProperty(String key, String value) { + SystemProperties.set(key, value); + } + + @Override + public int getSdkInt() { + return Build.VERSION.SDK_INT; } } @@ -1161,6 +1208,8 @@ public class PackageManagerService extends IPackageManager.Stub public @Nullable String retailDemoPackage; public ComponentName resolveComponentName; public ArrayMap<String, AndroidPackage> packages; + public boolean enableFreeCacheV2; + public int sdkVersion; } private final AppsFilter mAppsFilter; @@ -1354,7 +1403,7 @@ public class PackageManagerService extends IPackageManager.Stub options.setTemporaryAppWhitelistDuration(whitelistTimeout); DeviceIdleInternal idleController = - mInjector.getLocalDeviceIdleController(); + mInjector.getLocalService(DeviceIdleInternal.class); idleController.addPowerSaveTempWhitelistApp(Process.myUid(), mIntentFilterVerifierComponent.getPackageName(), whitelistTimeout, UserHandle.USER_SYSTEM, true, "intent filter verifier"); @@ -1427,7 +1476,7 @@ public class PackageManagerService extends IPackageManager.Stub case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS: if (!verified) { // Don't demote if sysconfig says 'always' - SystemConfig systemConfig = SystemConfig.getInstance(); + SystemConfig systemConfig = mInjector.getSystemConfig(); ArraySet<String> packages = systemConfig.getLinkedApps(); if (!packages.contains(packageName)) { // updatedStatus is already UNDEFINED @@ -2324,7 +2373,8 @@ public class PackageManagerService extends IPackageManager.Stub // Send broadcast package appeared if external for all users if (res.pkg.isExternalStorage()) { if (!update) { - final StorageManager storage = mInjector.getStorageManager(); + final StorageManager storage = mInjector.getSystemService( + StorageManager.class); VolumeInfo volume = storage.findVolumeByUuid( res.pkg.getStorageUuid().toString()); @@ -2503,7 +2553,7 @@ public class PackageManagerService extends IPackageManager.Stub ApkChecksums.Injector injector = new ApkChecksums.Injector( () -> mContext, () -> mInjector.getBackgroundHandler(), - () -> mContext.getSystemService(IncrementalManager.class)); + () -> mInjector.getIncrementalManager()); ApkChecksums.getChecksums(filesToChecksum, optional, required, trustedCerts, statusReceiver, injector); }); @@ -2679,31 +2729,32 @@ public class PackageManagerService extends IPackageManager.Stub Injector injector = new Injector( context, lock, installer, installLock, new PackageAbiHelperImpl(), backgroundHandler, - (i, pm) -> - new ComponentResolver(i.getUserManagerService(), pm.mPmInternal, lock), - (i, pm) -> - PermissionManagerService.create(context, lock), - (i, pm) -> - new UserManagerService(context, pm, + (i, pm) -> new ComponentResolver(i.getUserManagerService(), pm.mPmInternal, lock), + (i, pm) -> PermissionManagerService.create(context, lock), + (i, pm) -> new UserManagerService(context, pm, new UserDataPreparer(installer, installLock, context, onlyCore), lock), - (i, pm) -> - new Settings(Environment.getDataDirectory(), - i.getPermissionManagerServiceInternal().getPermissionSettings(), - RuntimePermissionsPersistence.createInstance(), - i.getPermissionManagerServiceInternal(), lock), - new Injector.LocalServicesProducer<>(ActivityTaskManagerInternal.class), - new Injector.LocalServicesProducer<>(ActivityManagerInternal.class), - new Injector.LocalServicesProducer<>(DeviceIdleInternal.class), - new Injector.LocalServicesProducer<>(StorageManagerInternal.class), - new Injector.LocalServicesProducer<>(NetworkPolicyManagerInternal.class), - new Injector.LocalServicesProducer<>(PermissionPolicyInternal.class), - new Injector.LocalServicesProducer<>(DeviceStorageMonitorInternal.class), - new Injector.SystemServiceProducer<>(DisplayManager.class), - new Injector.SystemServiceProducer<>(StorageManager.class), - new Injector.SystemServiceProducer<>(AppOpsManager.class), + (i, pm) -> new Settings(Environment.getDataDirectory(), + i.getPermissionManagerServiceInternal().getPermissionSettings(), + RuntimePermissionsPersistence.createInstance(), + i.getPermissionManagerServiceInternal(), lock), (i, pm) -> AppsFilter.create(pm.mPmInternal, i), - (i, pm) -> (PlatformCompat) ServiceManager.getService("platform_compat")); + (i, pm) -> (PlatformCompat) ServiceManager.getService("platform_compat"), + (i, pm) -> SystemConfig.getInstance(), + (i, pm) -> new PackageDexOptimizer(i.getInstaller(), i.getInstallLock(), + i.getContext(), "*dexopt*"), + (i, pm) -> new DexManager(i.getContext(), pm, i.getPackageDexOptimizer(), + i.getInstaller(), i.getInstallLock()), + (i, pm) -> new ArtManagerService(i.getContext(), pm, i.getInstaller(), + i.getInstallLock()), + (i, pm) -> ApexManager.getInstance(), + (i, pm) -> (IPermissionManager) ServiceManager.getService("permissionmgr"), + (i, pm) -> new ViewCompiler(i.getInstallLock(), i.getInstaller()), + (i, pm) -> (IncrementalManager) + pm.mContext.getSystemService(Context.INCREMENTAL_SERVICE), + new DefaultSystemWrapper(), + LocalServices::getService, + context::getSystemService); PackageManagerService m = new PackageManagerService(injector, onlyCore, factoryTest); t.traceEnd(); // "create package manager" @@ -2776,16 +2827,17 @@ public class PackageManagerService extends IPackageManager.Stub * reasons. This simply requests that the copy takes place and awaits confirmation of its * completion. See platform/system/extras/cppreopt/ for the implementation of the actual copy. */ - private static void requestCopyPreoptedFiles() { + private static void requestCopyPreoptedFiles(Injector injector) { final int WAIT_TIME_MS = 100; final String CP_PREOPT_PROPERTY = "sys.cppreopt"; - if (SystemProperties.getInt("ro.cp_system_other_odex", 0) == 1) { - SystemProperties.set(CP_PREOPT_PROPERTY, "requested"); + if (injector.getSystemWrapper().getPropertyInt("ro.cp_system_other_odex", 0) == 1) { + injector.getSystemWrapper().setProperty(CP_PREOPT_PROPERTY, "requested"); // We will wait for up to 100 seconds. final long timeStart = SystemClock.uptimeMillis(); final long timeEnd = timeStart + 100 * 1000; long timeNow = timeStart; - while (!SystemProperties.get(CP_PREOPT_PROPERTY).equals("finished")) { + while (!injector.getSystemWrapper() + .getProperty(CP_PREOPT_PROPERTY).equals("finished")) { try { Thread.sleep(WAIT_TIME_MS); } catch (InterruptedException e) { @@ -2793,7 +2845,7 @@ public class PackageManagerService extends IPackageManager.Stub } timeNow = SystemClock.uptimeMillis(); if (timeNow > timeEnd) { - SystemProperties.set(CP_PREOPT_PROPERTY, "timed-out"); + injector.getSystemWrapper().setProperty(CP_PREOPT_PROPERTY, "timed-out"); Slog.wtf(TAG, "cppreopt did not finish!"); break; } @@ -2925,6 +2977,8 @@ public class PackageManagerService extends IPackageManager.Stub mResolveComponentName = testParams.resolveComponentName; mPackages.putAll(testParams.packages); + mEnableFreeCacheV2 = testParams.enableFreeCacheV2; + mSdkVersion = testParams.sdkVersion; } public PackageManagerService(Injector injector, boolean onlyCore, boolean factoryTest) { @@ -2950,6 +3004,7 @@ public class PackageManagerService extends IPackageManager.Stub LockGuard.installLock(mLock, LockGuard.INDEX_PACKAGES); EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_START, SystemClock.uptimeMillis()); + mSdkVersion = injector.getSystemWrapper().getSdkInt(); if (mSdkVersion <= 0) { Slog.w(TAG, "**** ro.build.version.sdk not set!"); @@ -2960,6 +3015,8 @@ public class PackageManagerService extends IPackageManager.Stub mOnlyCore = onlyCore; mMetrics = new DisplayMetrics(); mInstaller = injector.getInstaller(); + mEnableFreeCacheV2 = + injector.getSystemWrapper().getPropertyBoolean("fw.free_cache_v2", true); // Create sub-components that provide services / data. Order here is important. t.traceBegin("createSubComponents"); @@ -2971,9 +3028,8 @@ public class PackageManagerService extends IPackageManager.Stub mComponentResolver = injector.getComponentResolver(); mPermissionManager = injector.getPermissionManagerServiceInternal(); mSettings = injector.getSettings(); - mPermissionManagerService = (IPermissionManager) ServiceManager.getService("permissionmgr"); - mIncrementalManager = - (IncrementalManager) mContext.getSystemService(Context.INCREMENTAL_SERVICE); + mPermissionManagerService = injector.getPermissionManagerService(); + mIncrementalManager = mInjector.getIncrementalManager(); PlatformCompat platformCompat = mInjector.getCompatibility(); mPackageParserCallback = new PackageParser2.Callback() { @Override @@ -3009,7 +3065,8 @@ public class PackageManagerService extends IPackageManager.Stub ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED); t.traceEnd(); - String separateProcesses = SystemProperties.get("debug.separate_processes"); + String separateProcesses = + injector.getSystemWrapper().getProperty("debug.separate_processes"); if (separateProcesses != null && separateProcesses.length() > 0) { if ("*".equals(separateProcesses)) { mDefParseFlags = PackageParser.PARSE_IGNORE_PROCESSES; @@ -3026,25 +3083,22 @@ public class PackageManagerService extends IPackageManager.Stub mSeparateProcesses = null; } - mPackageDexOptimizer = new PackageDexOptimizer(mInstaller, mInstallLock, mContext, - "*dexopt*"); - mDexManager = - new DexManager(mContext, this, mPackageDexOptimizer, mInstaller, mInstallLock); - mArtManagerService = new ArtManagerService(mContext, this, mInstaller, mInstallLock); + mPackageDexOptimizer = injector.getPackageDexOptimizer(); + mDexManager = injector.getDexManager(); + mArtManagerService = injector.getArtManagerService(); mMoveCallbacks = new MoveCallbacks(FgThread.get().getLooper()); + mViewCompiler = injector.getViewCompiler(); - mViewCompiler = new ViewCompiler(mInstallLock, mInstaller); - - getDefaultDisplayMetrics(mInjector.getDisplayManager(), mMetrics); + getDefaultDisplayMetrics(mInjector.getSystemService(DisplayManager.class), mMetrics); t.traceBegin("get system config"); - SystemConfig systemConfig = SystemConfig.getInstance(); + SystemConfig systemConfig = injector.getSystemConfig(); mAvailableFeatures = systemConfig.getAvailableFeatures(); t.traceEnd(); mProtectedPackages = new ProtectedPackages(mContext); - mApexManager = ApexManager.getInstance(); + mApexManager = injector.getApexManager(); mAppsFilter = mInjector.getAppsFilter(); final List<ScanPartition> scanPartitions = new ArrayList<>(); @@ -3122,7 +3176,7 @@ public class PackageManagerService extends IPackageManager.Stub } if (!mOnlyCore && mFirstBoot) { - requestCopyPreoptedFiles(); + requestCopyPreoptedFiles(mInjector); } String customResolverActivityName = Resources.getSystem().getString( @@ -3177,7 +3231,7 @@ public class PackageManagerService extends IPackageManager.Stub } } - mCacheDir = preparePackageParserCache(); + mCacheDir = preparePackageParserCache(injector); // Set flag to monitor and not change apk file paths when // scanning install directories. @@ -4000,7 +4054,7 @@ public class PackageManagerService extends IPackageManager.Stub setUpInstantAppInstallerActivityLP(getInstantAppInstallerLPr()); } - private static @Nullable File preparePackageParserCache() { + private static @Nullable File preparePackageParserCache(Injector injector) { if (!FORCE_PACKAGE_PARSED_CACHE_ENABLED) { if (!DEFAULT_PACKAGE_PARSER_CACHE_ENABLED) { return null; @@ -4011,7 +4065,8 @@ public class PackageManagerService extends IPackageManager.Stub return null; } - if (SystemProperties.getBoolean("pm.boot.disable_package_cache", false)) { + if (injector.getSystemWrapper() + .getPropertyBoolean("pm.boot.disable_package_cache", false)) { Slog.i(TAG, "Disabling package parser cache due to system property."); return null; } @@ -4027,7 +4082,7 @@ public class PackageManagerService extends IPackageManager.Stub // identify cached items. In particular, changing the value of certain // feature flags should cause us to invalidate any caches. final String cacheName = FORCE_PACKAGE_PARSED_CACHE_ENABLED ? "debug" - : SystemProperties.digestOf( + : injector.getSystemWrapper().digestOfProperties( "ro.build.fingerprint", StorageManager.PROP_ISOLATED_STORAGE, StorageManager.PROP_ISOLATED_STORAGE_SNAPSHOT @@ -4093,7 +4148,7 @@ public class PackageManagerService extends IPackageManager.Stub public boolean isDeviceUpgrading() { // allow instant applications // The system property allows testing ota flow when upgraded to the same image. - return mIsUpgrade || SystemProperties.getBoolean( + return mIsUpgrade || mInjector.getSystemWrapper().getPropertyBoolean( "persist.pm.mock-upgrade", false /* default */); } @@ -4360,7 +4415,7 @@ public class PackageManagerService extends IPackageManager.Stub Slog.d(TAG, "Priming domain verifications in user " + userId); } - SystemConfig systemConfig = SystemConfig.getInstance(); + SystemConfig systemConfig = mInjector.getSystemConfig(); ArraySet<String> packages = systemConfig.getLinkedApps(); for (String packageName : packages) { @@ -5169,11 +5224,11 @@ public class PackageManagerService extends IPackageManager.Stub * until the requested bytes are available. */ public void freeStorage(String volumeUuid, long bytes, int storageFlags) throws IOException { - final StorageManager storage = mInjector.getStorageManager(); + final StorageManager storage = mInjector.getSystemService(StorageManager.class); final File file = storage.findPathForUuid(volumeUuid); if (file.getUsableSpace() >= bytes) return; - if (ENABLE_FREE_CACHE_V2) { + if (mEnableFreeCacheV2) { final boolean internalVolume = Objects.equals(StorageManager.UUID_PRIVATE_INTERNAL, volumeUuid); final boolean aggressive = (storageFlags @@ -5252,7 +5307,7 @@ public class PackageManagerService extends IPackageManager.Stub private boolean pruneUnusedStaticSharedLibraries(long neededSpace, long maxCachePeriod) throws IOException { - final StorageManager storage = mInjector.getStorageManager(); + final StorageManager storage = mInjector.getSystemService(StorageManager.class); final File volume = storage.findPathForUuid(StorageManager.UUID_PRIVATE_INTERNAL); List<VersionedPackage> packagesToDelete = null; @@ -5500,7 +5555,8 @@ public class PackageManagerService extends IPackageManager.Stub } private boolean isRecentsAccessingChildProfiles(int callingUid, int targetUserId) { - if (!mInjector.getActivityTaskManagerInternal().isCallerRecents(callingUid)) { + if (!mInjector.getLocalService(ActivityTaskManagerInternal.class) + .isCallerRecents(callingUid)) { return false; } final long token = Binder.clearCallingIdentity(); @@ -5983,7 +6039,7 @@ public class PackageManagerService extends IPackageManager.Stub res.addAll(mAvailableFeatures.values()); } final FeatureInfo fi = new FeatureInfo(); - fi.reqGlEsVersion = SystemProperties.getInt("ro.opengles.version", + fi.reqGlEsVersion = mInjector.getSystemWrapper().getPropertyInt("ro.opengles.version", FeatureInfo.GL_ES_VERSION_UNDEFINED); res.add(fi); @@ -9065,10 +9121,10 @@ public class PackageManagerService extends IPackageManager.Stub if (providerInfo != null) { // Looking for cross-user grants before enforcing the typical cross-users permissions if (userId != UserHandle.getUserId(callingUid)) { - final UriGrantsManagerInternal mUgmInternal = - LocalServices.getService(UriGrantsManagerInternal.class); + final UriGrantsManagerInternal ugmInternal = + mInjector.getLocalService(UriGrantsManagerInternal.class); checkedGrants = - mUgmInternal.checkAuthorityGrants(callingUid, providerInfo, userId, true); + ugmInternal.checkAuthorityGrants(callingUid, providerInfo, userId, true); } } if (!checkedGrants) { @@ -9678,7 +9734,7 @@ public class PackageManagerService extends IPackageManager.Stub pkgName, getSettingsVersionForPackage(parsedPackage)), Collections.singletonMap(pkgName, getSharedLibLatestVersionSetting(scanResult))), - mSettings.mKeySetManagerService); + mSettings.mKeySetManagerService, mInjector); appIdCreated = optimisticallyRegisterAppId(scanResult); commitReconciledScanResultLocked( reconcileResult.get(pkgName), mUserManager.getUserIds()); @@ -9696,6 +9752,18 @@ public class PackageManagerService extends IPackageManager.Stub mSettings.disableSystemPackageLPw(parsedPackage.getPackageName(), true); } } + if (mIncrementalManager != null && isIncrementalPath(parsedPackage.getPath())) { + if (pkgSetting != null && pkgSetting.isPackageLoading()) { + final StorageHealthCheckParams healthCheckParams = new StorageHealthCheckParams(); + healthCheckParams.blockedTimeoutMs = INCREMENTAL_STORAGE_BLOCKED_TIMEOUT_MS; + healthCheckParams.unhealthyTimeoutMs = INCREMENTAL_STORAGE_UNHEALTHY_TIMEOUT_MS; + healthCheckParams.unhealthyMonitoringMs = + INCREMENTAL_STORAGE_UNHEALTHY_MONITORING_MS; + mIncrementalManager.registerHealthListener(parsedPackage.getPath(), + healthCheckParams, + new IncrementalHealthListener(parsedPackage.getPackageName())); + } + } return scanResult.pkgSetting.pkg; } @@ -9957,7 +10025,7 @@ public class PackageManagerService extends IPackageManager.Stub pkgCompilationReason = PackageManagerService.REASON_BACKGROUND_DEXOPT; } - if (SystemProperties.getBoolean(PRECOMPILE_LAYOUTS, false)) { + if (mInjector.getSystemWrapper().getPropertyBoolean(PRECOMPILE_LAYOUTS, false)) { mArtManagerService.compileLayouts(pkg); } @@ -10933,7 +11001,7 @@ public class PackageManagerService extends IPackageManager.Stub } private int getVendorPartitionVersion() { - final String version = SystemProperties.get("ro.vndk.version"); + final String version = mInjector.getSystemWrapper().getProperty("ro.vndk.version"); if (!version.isEmpty()) { try { return Integer.parseInt(version); @@ -12349,9 +12417,10 @@ public class PackageManagerService extends IPackageManager.Stub // A non-preloaded overlay package, without <overlay android:targetName>, will // only be used if it is signed with the same certificate as its target OR if // it is signed with the same certificate as a reference package declared - // in 'config-signature' tag of SystemConfig. - // If the target is already installed or 'config-signature' tag in SystemConfig - // is set, check this here to augment the last line of defence which is OMS. + // in 'overlay-config-signature' tag of SystemConfig. + // If the target is already installed or 'overlay-config-signature' tag in + // SystemConfig is set, check this here to augment the last line of defense + // which is OMS. if (pkg.getOverlayTargetName() == null) { final PackageSetting targetPkgSetting = mSettings.getPackageLPr(pkg.getOverlayTarget()); @@ -12957,7 +13026,7 @@ public class PackageManagerService extends IPackageManager.Stub + intent.toShortString(false, true, false, false) + " " + intent.getExtras(), here); } - mInjector.getActivityManagerInternal().broadcastIntent( + mInjector.getLocalService(ActivityManagerInternal.class).broadcastIntent( intent, finishedReceiver, requiredPermissions, finishedReceiver != null, id, broadcastAllowList == null ? null : broadcastAllowList.get(id)); @@ -14262,7 +14331,7 @@ public class PackageManagerService extends IPackageManager.Stub && mInstantAppInstallerActivity.packageName.equals( mRequiredVerifierPackage)) { try { - mInjector.getAppOpsManager() + mInjector.getSystemService(AppOpsManager.class) .checkPackage(installerUid, mRequiredVerifierPackage); if (DEBUG_VERIFY) { Slog.i(TAG, "disable verification for instant app"); @@ -14512,7 +14581,7 @@ public class PackageManagerService extends IPackageManager.Stub if (getInstantAppPackageName(Binder.getCallingUid()) != null) { throw new SecurityException("Instant applications don't have access to this method"); } - mInjector.getAppOpsManager().checkPackage(Binder.getCallingUid(), + mInjector.getSystemService(AppOpsManager.class).checkPackage(Binder.getCallingUid(), callerPackageName); synchronized (mLock) { PackageSetting ps = mSettings.mPackages.get(packageName); @@ -14672,7 +14741,7 @@ public class PackageManagerService extends IPackageManager.Stub */ private boolean performRollbackManagerRestore(int userId, int token, PackageInstalledInfo res, PostInstallData data) { - RollbackManagerInternal rm = LocalServices.getService(RollbackManagerInternal.class); + RollbackManagerInternal rm = mInjector.getLocalService(RollbackManagerInternal.class); final String packageName = res.pkg.getPackageName(); final int[] allUsers = mUserManager.getUserIds(); @@ -15435,7 +15504,7 @@ public class PackageManagerService extends IPackageManager.Stub integrityVerification.setPackage("android"); DeviceIdleInternal idleController = - mInjector.getLocalDeviceIdleController(); + mInjector.getLocalService(DeviceIdleInternal.class); final long idleDuration = getVerificationTimeout(); idleController.addPowerSaveTempWhitelistAppDirect(Process.myUid(), @@ -15545,7 +15614,7 @@ public class PackageManagerService extends IPackageManager.Stub receivers, verificationState); DeviceIdleInternal idleController = - mInjector.getLocalDeviceIdleController(); + mInjector.getLocalService(DeviceIdleInternal.class); final long idleDuration = getVerificationTimeout(); final BroadcastOptions options = BroadcastOptions.makeBasic(); options.setTemporaryAppWhitelistDuration(idleDuration); @@ -16355,12 +16424,25 @@ public class PackageManagerService extends IPackageManager.Stub // TODO(b/169721400): generalize Incremental States and create a Callback object // that can be used for all the packages. - final IncrementalStatesCallback incrementalStatesCallback = - new IncrementalStatesCallback(ps, userId); final String codePath = ps.getPathString(); if (IncrementalManager.isIncrementalPath(codePath) && mIncrementalManager != null) { - mIncrementalManager.registerCallback(codePath, incrementalStatesCallback); + final IncrementalStatesCallback incrementalStatesCallback = + new IncrementalStatesCallback(ps.name, + UserHandle.getUid(userId, ps.appId), + getInstalledUsers(ps, userId)); ps.setIncrementalStatesCallback(incrementalStatesCallback); + mIncrementalManager.registerLoadingProgressCallback(codePath, + new IncrementalProgressListener(ps.name)); + final IncrementalHealthListener incrementalHealthListener = + new IncrementalHealthListener(ps.name); + final StorageHealthCheckParams healthCheckParams = + new StorageHealthCheckParams(); + healthCheckParams.blockedTimeoutMs = INCREMENTAL_STORAGE_BLOCKED_TIMEOUT_MS; + healthCheckParams.unhealthyTimeoutMs = INCREMENTAL_STORAGE_UNHEALTHY_TIMEOUT_MS; + healthCheckParams.unhealthyMonitoringMs = + INCREMENTAL_STORAGE_UNHEALTHY_MONITORING_MS; + mIncrementalManager.registerHealthListener(codePath, + new StorageHealthCheckParams(), incrementalHealthListener); } // Ensure that the uninstall reason is UNKNOWN for users with the package installed. @@ -16544,7 +16626,7 @@ public class PackageManagerService extends IPackageManager.Stub @GuardedBy("mLock") private static Map<String, ReconciledPackage> reconcilePackagesLocked( - final ReconcileRequest request, KeySetManagerService ksms) + final ReconcileRequest request, KeySetManagerService ksms, Injector injector) throws ReconcileFailure { final Map<String, ScanResult> scannedPackages = request.scannedPackages; @@ -16695,7 +16777,8 @@ public class PackageManagerService extends IPackageManager.Stub && compareSignatures(sharedUserSignatures, parsedPackage.getSigningDetails().signatures) != PackageManager.SIGNATURE_MATCH) { - if (SystemProperties.getInt("ro.product.first_api_level", 0) <= 29) { + if (injector.getSystemWrapper() + .getPropertyInt("ro.product.first_api_level", 0) <= 29) { // Mismatched signatures is an error and silently skipping system // packages will likely break the device in unforeseen ways. // However, we allow the device to boot anyway because, prior to Q, @@ -17069,7 +17152,7 @@ public class PackageManagerService extends IPackageManager.Stub try { Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "reconcilePackages"); reconciledPackages = reconcilePackagesLocked( - reconcileRequest, mSettings.mKeySetManagerService); + reconcileRequest, mSettings.mKeySetManagerService, mInjector); } catch (ReconcileFailure e) { for (InstallRequest request : requests) { request.installResult.setError("Reconciliation failed...", e); @@ -17204,7 +17287,7 @@ public class PackageManagerService extends IPackageManager.Stub if (performDexopt) { // Compile the layout resources. - if (SystemProperties.getBoolean(PRECOMPILE_LAYOUTS, false)) { + if (mInjector.getSystemWrapper().getPropertyBoolean(PRECOMPILE_LAYOUTS, false)) { Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "compileLayouts"); mViewCompiler.compileLayouts(pkg); Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); @@ -17263,45 +17346,39 @@ public class PackageManagerService extends IPackageManager.Stub NativeLibraryHelper.waitForNativeBinariesExtraction(incrementalStorages); } - private class IncrementalStatesCallback extends IPackageLoadingProgressCallback.Stub - implements IncrementalStates.Callback { - @GuardedBy("mPackageSetting") - private final PackageSetting mPackageSetting; - private final String mPackageName; - private final String mPathString; - private final int mUid; - private final int[] mInstalledUserIds; - - IncrementalStatesCallback(PackageSetting packageSetting, int userId) { - mPackageSetting = packageSetting; - mPackageName = packageSetting.name; - mUid = UserHandle.getUid(userId, packageSetting.appId); - mPathString = packageSetting.getPathString(); - final int[] allUserIds = resolveUserIds(userId); - final ArrayList<Integer> installedUserIds = new ArrayList<>(); - for (int i = 0; i < allUserIds.length; i++) { - if (packageSetting.getInstalled(allUserIds[i])) { - installedUserIds.add(allUserIds[i]); - } - } - final int numInstalledUserId = installedUserIds.size(); - mInstalledUserIds = new int[numInstalledUserId]; - for (int i = 0; i < numInstalledUserId; i++) { - mInstalledUserIds[i] = installedUserIds.get(i); + private int[] getInstalledUsers(PackageSetting ps, int userId) { + final int[] allUserIds = resolveUserIds(userId); + final ArrayList<Integer> installedUserIdsList = new ArrayList<>(); + for (int i = 0; i < allUserIds.length; i++) { + if (ps.getInstalled(allUserIds[i])) { + installedUserIdsList.add(allUserIds[i]); } } + final int numInstalledUserId = installedUserIdsList.size(); + final int[] installedUserIds = new int[numInstalledUserId]; + for (int i = 0; i < numInstalledUserId; i++) { + installedUserIds[i] = installedUserIdsList.get(i); + } + return installedUserIds; + } - @Override - public void onPackageLoadingProgressChanged(float progress) { - synchronized (mPackageSetting) { - mPackageSetting.setLoadingProgress(progress); - } + /** + * Package states callback, used to listen for package state changes and send broadcasts + */ + private final class IncrementalStatesCallback implements IncrementalStates.Callback { + private final String mPackageName; + private final int mUid; + private final int[] mInstalledUserIds; + IncrementalStatesCallback(String packageName, int uid, int[] installedUserIds) { + mPackageName = packageName; + mUid = uid; + mInstalledUserIds = installedUserIds; } @Override public void onPackageFullyLoaded() { - mIncrementalManager.unregisterCallback(mPathString, this); final SparseArray<int[]> newBroadcastAllowList; + final String codePath; synchronized (mLock) { final PackageSetting ps = mSettings.mPackages.get(mPackageName); if (ps == null) { @@ -17309,6 +17386,7 @@ public class PackageManagerService extends IPackageManager.Stub } newBroadcastAllowList = mAppsFilter.getVisibilityAllowList( ps, mInstalledUserIds, mSettings.mPackages); + codePath = ps.getPathString(); } Bundle extras = new Bundle(); extras.putInt(Intent.EXTRA_UID, mUid); @@ -17317,6 +17395,8 @@ public class PackageManagerService extends IPackageManager.Stub extras, 0 /*flags*/, null /*targetPackage*/, null /*finishedReceiver*/, mInstalledUserIds, null /* instantUserIds */, newBroadcastAllowList); + // Unregister health listener as it will always be healthy from now + mIncrementalManager.unregisterHealthListener(codePath); } @Override @@ -17333,7 +17413,7 @@ public class PackageManagerService extends IPackageManager.Stub Bundle extras = new Bundle(); extras.putInt(Intent.EXTRA_UID, mUid); extras.putString(Intent.EXTRA_PACKAGE_NAME, mPackageName); - extras.putInt(Intent.EXTRA_REASON, reason); + extras.putInt(Intent.EXTRA_UNSTARTABLE_REASON, reason); // send broadcast to users with this app installed sendPackageBroadcast(Intent.ACTION_PACKAGE_UNSTARTABLE, mPackageName, extras, 0 /*flags*/, @@ -17364,37 +17444,48 @@ public class PackageManagerService extends IPackageManager.Stub } /** - * This is an internal method that is used to indicate changes on the health status of the - * Incremental Storage used by an installed package with an associated user id. This might - * result in a change in the loading state of the package. + * Loading progress callback, used to listen for progress changes and update package setting */ - public void onStorageHealthStatusChanged(String packageName, int status, int userId) { - final int callingUid = Binder.getCallingUid(); - mPermissionManager.enforceCrossUserPermission( - callingUid, userId, true, false, - "onStorageHealthStatusChanged"); - final PackageSetting ps = getPackageSettingForUser(packageName, callingUid, userId); - if (ps == null) { - return; + private class IncrementalProgressListener extends IPackageLoadingProgressCallback.Stub { + private final String mPackageName; + IncrementalProgressListener(String packageName) { + mPackageName = packageName; + } + + @Override + public void onPackageLoadingProgressChanged(float progress) { + final PackageSetting ps; + synchronized (mLock) { + ps = mSettings.mPackages.get(mPackageName); + } + if (ps == null) { + return; + } + ps.setLoadingProgress(progress); } - ps.setStorageHealthStatus(status); } /** - * This is an internal method that is used to indicate changes on the stream status of the - * data loader used by an installed package with an associated user id. This might - * result in a change in the loading state of the package. + * Incremental storage health status callback, used to listen for monitoring changes and update + * package setting. */ - public void onStreamStatusChanged(String packageName, int status, int userId) { - final int callingUid = Binder.getCallingUid(); - mPermissionManager.enforceCrossUserPermission( - callingUid, userId, true, false, - "onStreamStatusChanged"); - final PackageSetting ps = getPackageSettingForUser(packageName, callingUid, userId); - if (ps == null) { - return; + private class IncrementalHealthListener extends IStorageHealthListener.Stub { + private final String mPackageName; + IncrementalHealthListener(String packageName) { + mPackageName = packageName; + } + + @Override + public void onHealthStatus(int storageId, int status) throws RemoteException { + final PackageSetting ps; + synchronized (mLock) { + ps = mSettings.mPackages.get(mPackageName); + } + if (ps == null) { + return; + } + ps.setStorageHealthStatus(status); } - ps.setStreamStatus(status); } @Nullable PackageSetting getPackageSettingForUser(String packageName, int callingUid, @@ -17756,7 +17847,7 @@ public class PackageManagerService extends IPackageManager.Stub // also includes the "updating the same package" case, of course. // "updating same package" could also involve key-rotation. final boolean sigsOk; - final String sourcePackageName = bp.getSourcePackageName(); + final String sourcePackageName = bp.getPackageName(); final PackageSetting sourcePackageSetting; synchronized (mLock) { sourcePackageSetting = mSettings.getPackageLPr(sourcePackageName); @@ -19839,7 +19930,7 @@ public class PackageManagerService extends IPackageManager.Stub } private void resetNetworkPolicies(int userId) { - mInjector.getNetworkPolicyManagerInternal().resetUserState(userId); + mInjector.getLocalService(NetworkPolicyManagerInternal.class).resetUserState(userId); } /** @@ -21105,7 +21196,7 @@ public class PackageManagerService extends IPackageManager.Stub } public String getOverlayConfigSignaturePackageName() { - return ensureSystemPackageName(SystemConfig.getInstance() + return ensureSystemPackageName(mInjector.getSystemConfig() .getOverlayConfigSignaturePackage()); } @@ -21875,7 +21966,7 @@ public class PackageManagerService extends IPackageManager.Stub mPermissionManager.updateAllPermissions(StorageManager.UUID_PRIVATE_INTERNAL, false); final PermissionPolicyInternal permissionPolicyInternal = - mInjector.getPermissionPolicyInternal(); + mInjector.getLocalService(PermissionPolicyInternal.class); permissionPolicyInternal.setOnInitializedCallback(userId -> { // The SDK updated case is already handled when we run during the ctor. synchronized (mLock) { @@ -21886,13 +21977,13 @@ public class PackageManagerService extends IPackageManager.Stub } // Watch for external volumes that come and go over time - final StorageManager storage = mInjector.getStorageManager(); + final StorageManager storage = mInjector.getSystemService(StorageManager.class); storage.registerListener(mStorageListener); mInstallerService.systemReady(); mPackageDexOptimizer.systemReady(); - mInjector.getStorageManagerInternal().addExternalStoragePolicy( + mInjector.getLocalService(StorageManagerInternal.class).addExternalStoragePolicy( new StorageManagerInternal.ExternalStorageMountPolicy() { @Override public int getMountMode(int uid, String packageName) { @@ -22625,7 +22716,7 @@ public class PackageManagerService extends IPackageManager.Stub if (ArrayUtils.isEmpty(apkList)) { return; } - String sku = SystemProperties.get("ro.boot.hardware.sku"); + String sku = mInjector.getSystemWrapper().getProperty("ro.boot.hardware.sku"); if (!TextUtils.isEmpty(sku) && ArrayUtils.contains(skuArray, sku)) { return; } @@ -22921,7 +23012,7 @@ public class PackageManagerService extends IPackageManager.Stub } // Reconcile app data for all started/unlocked users - final StorageManager sm = mInjector.getStorageManager(); + final StorageManager sm = mInjector.getSystemService(StorageManager.class); UserManagerInternal umInternal = mInjector.getUserManagerInternal(); for (UserInfo user : mUserManager.getUsers(false /* includeDying */)) { final int flags; @@ -23122,7 +23213,7 @@ public class PackageManagerService extends IPackageManager.Stub * correct for all installed apps on all mounted volumes. */ void reconcileAppsData(int userId, int flags, boolean migrateAppsData) { - final StorageManager storage = mInjector.getStorageManager(); + final StorageManager storage = mInjector.getSystemService(StorageManager.class); for (VolumeInfo vol : storage.getWritablePrivateVolumes()) { final String volumeUuid = vol.getFsUuid(); synchronized (mInstallLock) { @@ -23257,7 +23348,7 @@ public class PackageManagerService extends IPackageManager.Stub Installer.Batch batch = new Installer.Batch(); UserManagerInternal umInternal = mInjector.getUserManagerInternal(); - StorageManagerInternal smInternal = mInjector.getStorageManagerInternal(); + StorageManagerInternal smInternal = mInjector.getLocalService(StorageManagerInternal.class); for (UserInfo user : mUserManager.getUsers(false /*excludeDying*/)) { final int flags; if (umInternal.isUserUnlockingOrUnlocked(user.id)) { @@ -23586,7 +23677,7 @@ public class PackageManagerService extends IPackageManager.Stub private void movePackageInternal(final String packageName, final String volumeUuid, final int moveId, final int callingUid, UserHandle user) throws PackageManagerException { - final StorageManager storage = mInjector.getStorageManager(); + final StorageManager storage = mInjector.getSystemService(StorageManager.class); final PackageManager pm = mContext.getPackageManager(); final String currentVolumeUuid; @@ -23824,7 +23915,7 @@ public class PackageManagerService extends IPackageManager.Stub return; } - final StorageManager storage = mInjector.getStorageManager();; + final StorageManager storage = mInjector.getSystemService(StorageManager.class);; VolumeInfo volume = storage.findVolumeByUuid(pkg.getStorageUuid().toString()); int packageExternalStorageType = getPackageExternalStorageType(volume, pkg.isExternalStorage()); @@ -23864,7 +23955,7 @@ public class PackageManagerService extends IPackageManager.Stub } }; - final StorageManager storage = mInjector.getStorageManager(); + final StorageManager storage = mInjector.getSystemService(StorageManager.class); storage.setPrimaryStorageUuid(volumeUuid, callback); return realMoveId; } @@ -24044,7 +24135,7 @@ public class PackageManagerService extends IPackageManager.Stub final long token = Binder.clearCallingIdentity(); try { final DeviceStorageMonitorInternal - dsm = mInjector.getDeviceStorageMonitorInternal(); + dsm = mInjector.getLocalService(DeviceStorageMonitorInternal.class); if (dsm != null) { return dsm.isMemoryLow(); } else { @@ -25636,7 +25727,7 @@ public class PackageManagerService extends IPackageManager.Stub "Failed registering loading progress callback. Incremental is not enabled"); return false; } - return mIncrementalManager.registerCallback(ps.getPathString(), + return mIncrementalManager.registerLoadingProgressCallback(ps.getPathString(), (IPackageLoadingProgressCallback) callback.getBinder()); } @@ -25655,7 +25746,7 @@ public class PackageManagerService extends IPackageManager.Stub if (mIncrementalManager == null) { return false; } - return mIncrementalManager.unregisterCallback(ps.getPathString(), + return mIncrementalManager.unregisterLoadingProgressCallback(ps.getPathString(), (IPackageLoadingProgressCallback) callback.getBinder()); } } @@ -25828,7 +25919,7 @@ public class PackageManagerService extends IPackageManager.Stub @Override public boolean isAutoRevokeWhitelisted(String packageName) { - int mode = mInjector.getAppOpsManager().checkOpNoThrow( + int mode = mInjector.getSystemService(AppOpsManager.class).checkOpNoThrow( AppOpsManager.OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED, Binder.getCallingUid(), packageName); return mode == MODE_IGNORED; diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java index c356fc72379f..9aa1a621a760 100644 --- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java +++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java @@ -24,6 +24,7 @@ import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATIO import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED; import android.accounts.IAccountManager; +import android.annotation.UserIdInt; import android.app.ActivityManager; import android.app.ActivityManagerInternal; import android.app.role.IRoleManager; @@ -2686,9 +2687,18 @@ class PackageManagerShellCommand extends ShellCommand { } } + // pm remove-user [--set-ephemeral-if-in-use] USER_ID public int runRemoveUser() throws RemoteException { int userId; - String arg = getNextArg(); + String arg; + boolean setEphemeralIfInUse = false; + while ((arg = getNextOption()) != null) { + if (arg.equals("--set-ephemeral-if-in-use")) { + setEphemeralIfInUse = true; + } + } + + arg = getNextArg(); if (arg == null) { getErrPrintWriter().println("Error: no user id specified."); return 1; @@ -2696,6 +2706,15 @@ class PackageManagerShellCommand extends ShellCommand { userId = UserHandle.parseUserArg(arg); IUserManager um = IUserManager.Stub.asInterface( ServiceManager.getService(Context.USER_SERVICE)); + if (setEphemeralIfInUse) { + return removeUserOrSetEphemeral(um, userId); + } else { + return removeUser(um, userId); + } + } + + private int removeUser(IUserManager um, @UserIdInt int userId) throws RemoteException { + Slog.i(TAG, "Removing user " + userId); if (um.removeUser(userId)) { getOutPrintWriter().println("Success: removed user"); return 0; @@ -2705,6 +2724,27 @@ class PackageManagerShellCommand extends ShellCommand { } } + private int removeUserOrSetEphemeral(IUserManager um, @UserIdInt int userId) + throws RemoteException { + Slog.i(TAG, "Removing " + userId + " or set as ephemeral if in use."); + int result = um.removeUserOrSetEphemeral(userId); + switch (result) { + case UserManager.REMOVE_RESULT_REMOVED: + getOutPrintWriter().printf("Success: user %d removed\n", userId); + return 0; + case UserManager.REMOVE_RESULT_SET_EPHEMERAL: + getOutPrintWriter().printf("Success: user %d set as ephemeral\n", userId); + return 0; + case UserManager.REMOVE_RESULT_ALREADY_BEING_REMOVED: + getOutPrintWriter().printf("Success: user %d is already being removed\n", userId); + return 0; + default: + getErrPrintWriter().printf("Error: couldn't remove or mark ephemeral user id %d\n", + userId); + return 1; + } + } + public int runSetUserRestriction() throws RemoteException { int userId = UserHandle.USER_SYSTEM; String opt = getNextOption(); @@ -3769,9 +3809,13 @@ class PackageManagerShellCommand extends ShellCommand { pw.println(" --restricted is shorthand for '--user-type android.os.usertype.full.RESTRICTED'."); pw.println(" --guest is shorthand for '--user-type android.os.usertype.full.GUEST'."); pw.println(""); - pw.println(" remove-user USER_ID"); + pw.println(" remove-user [--set-ephemeral-if-in-use] USER_ID"); pw.println(" Remove the user with the given USER_IDENTIFIER, deleting all data"); - pw.println(" associated with that user"); + pw.println(" associated with that user."); + pw.println(" --set-ephemeral-if-in-use: If the user is currently running and"); + pw.println(" therefore cannot be removed immediately, mark the user as ephemeral"); + pw.println(" so that it will be automatically removed when possible (after user"); + pw.println(" switch or reboot)"); pw.println(""); pw.println(" set-user-restriction [--user USER_ID] RESTRICTION VALUE"); pw.println(""); diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java index d52ad46d4b7e..be7c7c6ff1d6 100644 --- a/services/core/java/com/android/server/pm/PackageSettingBase.java +++ b/services/core/java/com/android/server/pm/PackageSettingBase.java @@ -793,13 +793,6 @@ public abstract class PackageSettingBase extends SettingBase { incrementalStates.onStorageHealthStatusChanged(status); } - /** - * @see IncrementalStates#onStreamStatusChanged(int) - */ - public void setStreamStatus(int status) { - incrementalStates.onStreamStatusChanged(status); - } - protected PackageSettingBase updateFrom(PackageSettingBase other) { super.copyFrom(other); setPath(other.getPath()); diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index 3ec156f6a3a1..a0344e27f96c 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -110,7 +110,6 @@ import com.android.internal.widget.LockPatternUtils; import com.android.server.LocalServices; import com.android.server.LockGuard; import com.android.server.SystemService; -import com.android.server.SystemService.TargetUser; import com.android.server.am.UserState; import com.android.server.storage.DeviceStorageMonitorInternal; import com.android.server.utils.TimingsTraceAndSlog; @@ -253,9 +252,17 @@ public class UserManagerService extends IUserManager.Stub { private final Context mContext; private final PackageManagerService mPm; + + /** + * Lock for packages. If using with {@link #mUsersLock}, {@link #mPackagesLock} should be + * acquired first. + */ private final Object mPackagesLock; private final UserDataPreparer mUserDataPreparer; - // Short-term lock for internal state, when interaction/sync with PM is not required + /** + * Short-term lock for internal state, when interaction/sync with PM is not required. If using + * with {@link #mPackagesLock}, {@link #mPackagesLock} should be acquired first. + */ private final Object mUsersLock = LockGuard.installNewLock(LockGuard.INDEX_USER); private final Object mRestrictionsLock = new Object(); // Used for serializing access to app restriction files @@ -3859,16 +3866,10 @@ public class UserManagerService extends IUserManager.Stub { */ @Override public boolean removeUser(@UserIdInt int userId) { - Slog.i(LOG_TAG, "removeUser u" + userId); + Slog.i(LOG_TAG, "removeUser u" + userId, new Exception()); checkManageOrCreateUsersPermission("Only the system can remove users"); - final boolean isManagedProfile; - synchronized (mUsersLock) { - UserInfo userInfo = getUserInfoLU(userId); - isManagedProfile = userInfo != null && userInfo.isManagedProfile(); - } - String restriction = isManagedProfile - ? UserManager.DISALLOW_REMOVE_MANAGED_PROFILE : UserManager.DISALLOW_REMOVE_USER; + final String restriction = getUserRemovalRestriction(userId); if (getUserRestrictions(UserHandle.getCallingUserId()).getBoolean(restriction, false)) { Slog.w(LOG_TAG, "Cannot remove user. " + restriction + " is enabled."); return false; @@ -3882,6 +3883,21 @@ public class UserManagerService extends IUserManager.Stub { return removeUserUnchecked(userId); } + /** + * Returns the string name of the restriction to check for user removal. The restriction name + * varies depending on whether the user is a managed profile. + */ + private String getUserRemovalRestriction(@UserIdInt int userId) { + final boolean isManagedProfile; + final UserInfo userInfo; + synchronized (mUsersLock) { + userInfo = getUserInfoLU(userId); + } + isManagedProfile = userInfo != null && userInfo.isManagedProfile(); + return isManagedProfile + ? UserManager.DISALLOW_REMOVE_MANAGED_PROFILE : UserManager.DISALLOW_REMOVE_USER; + } + private boolean removeUserUnchecked(@UserIdInt int userId) { final long ident = Binder.clearCallingIdentity(); try { @@ -3974,6 +3990,64 @@ public class UserManagerService extends IUserManager.Stub { } } + @Override + public @UserManager.RemoveResult int removeUserOrSetEphemeral(@UserIdInt int userId) { + Slog.i(LOG_TAG, "removeUserOrSetEphemeral u" + userId); + checkManageUsersPermission("Only the system can remove users"); + final String restriction = getUserRemovalRestriction(userId); + if (getUserRestrictions(UserHandle.getCallingUserId()).getBoolean(restriction, false)) { + Slog.w(LOG_TAG, "Cannot remove user. " + restriction + " is enabled."); + return UserManager.REMOVE_RESULT_ERROR; + } + if (userId == UserHandle.USER_SYSTEM) { + Slog.e(LOG_TAG, "System user cannot be removed."); + return UserManager.REMOVE_RESULT_ERROR; + } + + final long ident = Binder.clearCallingIdentity(); + try { + final UserData userData; + synchronized (mPackagesLock) { + synchronized (mUsersLock) { + userData = mUsers.get(userId); + if (userData == null) { + Slog.e(LOG_TAG, + "Cannot remove user " + userId + ", invalid user id provided."); + return UserManager.REMOVE_RESULT_ERROR; + } + + if (mRemovingUserIds.get(userId)) { + Slog.e(LOG_TAG, "User " + userId + " is already scheduled for removal."); + return UserManager.REMOVE_RESULT_ALREADY_BEING_REMOVED; + } + } + + // Attempt to immediately remove a non-current user + final int currentUser = ActivityManager.getCurrentUser(); + if (currentUser != userId) { + // Attempt to remove the user. This will fail if the user is the current user + if (removeUser(userId)) { + return UserManager.REMOVE_RESULT_REMOVED; + } + + Slog.w(LOG_TAG, "Unable to immediately remove non-current user: " + userId + + ". User is still set as ephemeral and will be removed on user " + + "switch or reboot."); + } + + // If the user was not immediately removed, make sure it is marked as ephemeral. + // Don't mark as disabled since, per UserInfo.FLAG_DISABLED documentation, an + // ephemeral user should only be marked as disabled when its removal is in progress. + userData.info.flags |= UserInfo.FLAG_EPHEMERAL; + writeUserLP(userData); + + return UserManager.REMOVE_RESULT_SET_EPHEMERAL; + } + } finally { + Binder.restoreCallingIdentity(ident); + } + } + void finishRemoveUser(final @UserIdInt int userId) { if (DBG) Slog.i(LOG_TAG, "finishRemoveUser " + userId); diff --git a/services/core/java/com/android/server/pm/permission/BasePermission.java b/services/core/java/com/android/server/pm/permission/BasePermission.java index c08601740158..667414e9cc72 100644 --- a/services/core/java/com/android/server/pm/permission/BasePermission.java +++ b/services/core/java/com/android/server/pm/permission/BasePermission.java @@ -29,17 +29,17 @@ import static com.android.server.pm.Settings.TAG_ITEM; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; +import android.content.pm.PackageManager; import android.content.pm.PackageManagerInternal; import android.content.pm.PermissionInfo; import android.content.pm.parsing.component.ParsedPermission; +import android.os.Build; import android.os.UserHandle; import android.util.Log; import android.util.Slog; import com.android.server.pm.DumpState; import com.android.server.pm.PackageManagerService; -import com.android.server.pm.PackageSettingBase; -import com.android.server.pm.parsing.PackageInfoUtils; import com.android.server.pm.parsing.pkg.AndroidPackage; import libcore.util.EmptyArray; @@ -80,269 +80,287 @@ public final class BasePermission { @Retention(RetentionPolicy.SOURCE) public @interface ProtectionLevel {} - final String name; + @NonNull + private final String mName; - final @PermissionType int type; + private final @PermissionType int mType; - String sourcePackageName; + private String mPackageName; - int protectionLevel; + private int mProtectionLevel; - ParsedPermission perm; + @Nullable + private PermissionInfo mPermissionInfo; - PermissionInfo pendingPermissionInfo; + @Nullable + private PermissionInfo mPendingPermissionInfo; /** UID that owns the definition of this permission */ - int uid; + private int mUid; /** Additional GIDs given to apps granted this permission */ @NonNull - private int[] gids = EmptyArray.INT; + private int[] mGids = EmptyArray.INT; /** - * Flag indicating that {@link #gids} should be adjusted based on the + * Flag indicating that {@link #mGids} should be adjusted based on the * {@link UserHandle} the granted app is running as. */ - private boolean perUser; + private boolean mGidsPerUser; - public BasePermission(String _name, String _sourcePackageName, @PermissionType int _type) { - name = _name; - sourcePackageName = _sourcePackageName; - type = _type; + public BasePermission(@NonNull String name, String packageName, @PermissionType int type) { + mName = name; + mPackageName = packageName; + mType = type; // Default to most conservative protection level. - protectionLevel = PermissionInfo.PROTECTION_SIGNATURE; + mProtectionLevel = PermissionInfo.PROTECTION_SIGNATURE; } @Override public String toString() { - return "BasePermission{" + Integer.toHexString(System.identityHashCode(this)) + " " + name + return "BasePermission{" + Integer.toHexString(System.identityHashCode(this)) + " " + mName + "}"; } + @NonNull public String getName() { - return name; + return mName; } + public int getProtectionLevel() { - return protectionLevel; + return mProtectionLevel; } - public String getSourcePackageName() { - return sourcePackageName; + + public String getPackageName() { + return mPackageName; } + public int getType() { - return type; + return mType; } + public int getUid() { - return uid; + return mUid; } - public void setGids(@NonNull int[] gids, boolean perUser) { - this.gids = gids; - this.perUser = perUser; + + public void setGids(@NonNull int[] gids, boolean gidsPerUser) { + mGids = gids; + mGidsPerUser = gidsPerUser; } - public void setPermission(@Nullable ParsedPermission perm) { - this.perm = perm; + + public void setPermissionInfo(@Nullable PermissionInfo permissionInfo) { + mPermissionInfo = permissionInfo; } public boolean hasGids() { - return gids.length != 0; + return mGids.length != 0; } @NonNull public int[] computeGids(int userId) { - if (perUser) { - final int[] userGids = new int[gids.length]; - for (int i = 0; i < gids.length; i++) { - final int gid = gids[i]; + if (mGidsPerUser) { + final int[] userGids = new int[mGids.length]; + for (int i = 0; i < mGids.length; i++) { + final int gid = mGids[i]; userGids[i] = UserHandle.getUid(userId, gid); } return userGids; } else { - return gids.length != 0 ? gids.clone() : gids; + return mGids.length != 0 ? mGids.clone() : mGids; } } public int calculateFootprint(BasePermission perm) { - if (uid == perm.uid) { - return perm.name.length() + perm.perm.calculateFootprint(); + if (mUid == perm.mUid) { + return perm.mName.length() + perm.mPermissionInfo.calculateFootprint(); } return 0; } public boolean isPermission(ParsedPermission perm) { - if (this.perm == null) { + if (mPermissionInfo == null) { return false; } - return Objects.equals(this.perm.getPackageName(), perm.getPackageName()) - && Objects.equals(this.perm.getName(), perm.getName()); + return Objects.equals(mPermissionInfo.packageName, perm.getPackageName()) + && Objects.equals(mPermissionInfo.name, perm.getName()); } public boolean isDynamic() { - return type == TYPE_DYNAMIC; + return mType == TYPE_DYNAMIC; } - public boolean isNormal() { - return (protectionLevel & PermissionInfo.PROTECTION_MASK_BASE) + return (mProtectionLevel & PermissionInfo.PROTECTION_MASK_BASE) == PermissionInfo.PROTECTION_NORMAL; } public boolean isRuntime() { - return (protectionLevel & PermissionInfo.PROTECTION_MASK_BASE) + return (mProtectionLevel & PermissionInfo.PROTECTION_MASK_BASE) == PermissionInfo.PROTECTION_DANGEROUS; } + public boolean isInstalled() { + return mPermissionInfo != null + && (mPermissionInfo.flags & PermissionInfo.FLAG_INSTALLED) != 0; + } + public boolean isRemoved() { - return perm != null && (perm.getFlags() & PermissionInfo.FLAG_REMOVED) != 0; + return mPermissionInfo != null + && (mPermissionInfo.flags & PermissionInfo.FLAG_REMOVED) != 0; } public boolean isSoftRestricted() { - return perm != null && (perm.getFlags() & PermissionInfo.FLAG_SOFT_RESTRICTED) != 0; + return mPermissionInfo != null + && (mPermissionInfo.flags & PermissionInfo.FLAG_SOFT_RESTRICTED) != 0; } public boolean isHardRestricted() { - return perm != null && (perm.getFlags() & PermissionInfo.FLAG_HARD_RESTRICTED) != 0; + return mPermissionInfo != null + && (mPermissionInfo.flags & PermissionInfo.FLAG_HARD_RESTRICTED) != 0; } public boolean isHardOrSoftRestricted() { - return perm != null && (perm.getFlags() & (PermissionInfo.FLAG_HARD_RESTRICTED - | PermissionInfo.FLAG_SOFT_RESTRICTED)) != 0; + return mPermissionInfo != null && (mPermissionInfo.flags + & (PermissionInfo.FLAG_HARD_RESTRICTED | PermissionInfo.FLAG_SOFT_RESTRICTED)) != 0; } public boolean isImmutablyRestricted() { - return perm != null && (perm.getFlags() & PermissionInfo.FLAG_IMMUTABLY_RESTRICTED) != 0; + return mPermissionInfo != null + && (mPermissionInfo.flags & PermissionInfo.FLAG_IMMUTABLY_RESTRICTED) != 0; } public boolean isInstallerExemptIgnored() { - return perm != null - && (perm.getFlags() & PermissionInfo.FLAG_INSTALLER_EXEMPT_IGNORED) != 0; + return mPermissionInfo != null + && (mPermissionInfo.flags & PermissionInfo.FLAG_INSTALLER_EXEMPT_IGNORED) != 0; } public boolean isSignature() { - return (protectionLevel & PermissionInfo.PROTECTION_MASK_BASE) == - PermissionInfo.PROTECTION_SIGNATURE; + return (mProtectionLevel & PermissionInfo.PROTECTION_MASK_BASE) + == PermissionInfo.PROTECTION_SIGNATURE; } public boolean isAppOp() { - return (protectionLevel & PermissionInfo.PROTECTION_FLAG_APPOP) != 0; + return (mProtectionLevel & PermissionInfo.PROTECTION_FLAG_APPOP) != 0; } public boolean isDevelopment() { return isSignature() - && (protectionLevel & PermissionInfo.PROTECTION_FLAG_DEVELOPMENT) != 0; + && (mProtectionLevel & PermissionInfo.PROTECTION_FLAG_DEVELOPMENT) != 0; } public boolean isInstaller() { - return (protectionLevel & PermissionInfo.PROTECTION_FLAG_INSTALLER) != 0; + return (mProtectionLevel & PermissionInfo.PROTECTION_FLAG_INSTALLER) != 0; } public boolean isInstant() { - return (protectionLevel & PermissionInfo.PROTECTION_FLAG_INSTANT) != 0; + return (mProtectionLevel & PermissionInfo.PROTECTION_FLAG_INSTANT) != 0; } public boolean isOEM() { - return (protectionLevel & PermissionInfo.PROTECTION_FLAG_OEM) != 0; + return (mProtectionLevel & PermissionInfo.PROTECTION_FLAG_OEM) != 0; } public boolean isPre23() { - return (protectionLevel & PermissionInfo.PROTECTION_FLAG_PRE23) != 0; + return (mProtectionLevel & PermissionInfo.PROTECTION_FLAG_PRE23) != 0; } public boolean isPreInstalled() { - return (protectionLevel & PermissionInfo.PROTECTION_FLAG_PREINSTALLED) != 0; + return (mProtectionLevel & PermissionInfo.PROTECTION_FLAG_PREINSTALLED) != 0; } public boolean isPrivileged() { - return (protectionLevel & PermissionInfo.PROTECTION_FLAG_PRIVILEGED) != 0; + return (mProtectionLevel & PermissionInfo.PROTECTION_FLAG_PRIVILEGED) != 0; } public boolean isRuntimeOnly() { - return (protectionLevel & PermissionInfo.PROTECTION_FLAG_RUNTIME_ONLY) != 0; + return (mProtectionLevel & PermissionInfo.PROTECTION_FLAG_RUNTIME_ONLY) != 0; } public boolean isSetup() { - return (protectionLevel & PermissionInfo.PROTECTION_FLAG_SETUP) != 0; + return (mProtectionLevel & PermissionInfo.PROTECTION_FLAG_SETUP) != 0; } public boolean isVerifier() { - return (protectionLevel & PermissionInfo.PROTECTION_FLAG_VERIFIER) != 0; + return (mProtectionLevel & PermissionInfo.PROTECTION_FLAG_VERIFIER) != 0; } public boolean isVendorPrivileged() { - return (protectionLevel & PermissionInfo.PROTECTION_FLAG_VENDOR_PRIVILEGED) != 0; + return (mProtectionLevel & PermissionInfo.PROTECTION_FLAG_VENDOR_PRIVILEGED) != 0; } public boolean isSystemTextClassifier() { - return (protectionLevel & PermissionInfo.PROTECTION_FLAG_SYSTEM_TEXT_CLASSIFIER) + return (mProtectionLevel & PermissionInfo.PROTECTION_FLAG_SYSTEM_TEXT_CLASSIFIER) != 0; } public boolean isWellbeing() { - return (protectionLevel & PermissionInfo.PROTECTION_FLAG_WELLBEING) != 0; + return (mProtectionLevel & PermissionInfo.PROTECTION_FLAG_WELLBEING) != 0; } public boolean isDocumenter() { - return (protectionLevel & PermissionInfo.PROTECTION_FLAG_DOCUMENTER) != 0; + return (mProtectionLevel & PermissionInfo.PROTECTION_FLAG_DOCUMENTER) != 0; } public boolean isConfigurator() { - return (protectionLevel & PermissionInfo.PROTECTION_FLAG_CONFIGURATOR) + return (mProtectionLevel & PermissionInfo.PROTECTION_FLAG_CONFIGURATOR) != 0; } public boolean isIncidentReportApprover() { - return (protectionLevel & PermissionInfo.PROTECTION_FLAG_INCIDENT_REPORT_APPROVER) != 0; + return (mProtectionLevel & PermissionInfo.PROTECTION_FLAG_INCIDENT_REPORT_APPROVER) != 0; } public boolean isAppPredictor() { - return (protectionLevel & PermissionInfo.PROTECTION_FLAG_APP_PREDICTOR) != 0; + return (mProtectionLevel & PermissionInfo.PROTECTION_FLAG_APP_PREDICTOR) != 0; } public boolean isCompanion() { - return (protectionLevel & PermissionInfo.PROTECTION_FLAG_COMPANION) != 0; + return (mProtectionLevel & PermissionInfo.PROTECTION_FLAG_COMPANION) != 0; } public boolean isRetailDemo() { - return (protectionLevel & PermissionInfo.PROTECTION_FLAG_RETAIL_DEMO) != 0; + return (mProtectionLevel & PermissionInfo.PROTECTION_FLAG_RETAIL_DEMO) != 0; } public void transfer(@NonNull String origPackageName, @NonNull String newPackageName) { - if (!origPackageName.equals(sourcePackageName)) { + if (!origPackageName.equals(mPackageName)) { return; } - sourcePackageName = newPackageName; - perm = null; - if (pendingPermissionInfo != null) { - pendingPermissionInfo.packageName = newPackageName; + mPackageName = newPackageName; + mPermissionInfo = null; + if (mPendingPermissionInfo != null) { + mPendingPermissionInfo.packageName = newPackageName; } - uid = 0; - gids = EmptyArray.INT; - perUser = false; + mUid = 0; + mGids = EmptyArray.INT; + mGidsPerUser = false; } public boolean addToTree(@ProtectionLevel int protectionLevel, - @NonNull PermissionInfo info, @NonNull BasePermission tree) { + @NonNull PermissionInfo permissionInfo, @NonNull BasePermission tree) { final boolean changed = - (this.protectionLevel != protectionLevel - || perm == null - || uid != tree.uid - || !Objects.equals(perm.getPackageName(), tree.perm.getPackageName()) - || !comparePermissionInfos(perm, info)); - this.protectionLevel = protectionLevel; - info = new PermissionInfo(info); - info.protectionLevel = protectionLevel; - perm = new ParsedPermission(tree.perm); - uid = tree.uid; + (mProtectionLevel != protectionLevel + || mPermissionInfo == null + || mUid != tree.mUid + || !Objects.equals(mPermissionInfo.packageName, + tree.mPermissionInfo.packageName) + || !comparePermissionInfos(mPermissionInfo, permissionInfo)); + mProtectionLevel = protectionLevel; + mPermissionInfo = new PermissionInfo(permissionInfo); + mPermissionInfo.protectionLevel = protectionLevel; + mPermissionInfo.packageName = tree.mPermissionInfo.packageName; + mUid = tree.mUid; return changed; } public void updateDynamicPermission(Collection<BasePermission> permissionTrees) { if (PackageManagerService.DEBUG_SETTINGS) Log.v(TAG, "Dynamic permission: name=" - + getName() + " pkg=" + getSourcePackageName() - + " info=" + pendingPermissionInfo); - if (pendingPermissionInfo != null) { - final BasePermission tree = findPermissionTree(permissionTrees, name); - if (tree != null && tree.perm != null) { - perm = new ParsedPermission(tree.perm, pendingPermissionInfo, - tree.perm.getPackageName(), name); - uid = tree.uid; + + getName() + " pkg=" + getPackageName() + + " info=" + mPendingPermissionInfo); + if (mPendingPermissionInfo != null) { + final BasePermission tree = findPermissionTree(permissionTrees, mName); + if (tree != null && tree.mPermissionInfo != null) { + mPermissionInfo = new PermissionInfo(mPendingPermissionInfo); + mPermissionInfo.packageName = tree.mPermissionInfo.packageName; + mPermissionInfo.name = mName; + mUid = tree.mUid; } } } static BasePermission createOrUpdate(PackageManagerInternal packageManagerInternal, - @Nullable BasePermission bp, @NonNull ParsedPermission p, + @Nullable BasePermission bp, @NonNull PermissionInfo p, @NonNull AndroidPackage pkg, Collection<BasePermission> permissionTrees, boolean chatty) { - final PackageSettingBase pkgSetting = - (PackageSettingBase) packageManagerInternal.getPackageSetting(pkg.getPackageName()); // Allow system apps to redefine non-system permissions - if (bp != null && !Objects.equals(bp.sourcePackageName, p.getPackageName())) { + if (bp != null && !Objects.equals(bp.mPackageName, p.packageName)) { final boolean currentOwnerIsSystem; - if (bp.perm == null) { + if (bp.mPermissionInfo == null) { currentOwnerIsSystem = false; } else { AndroidPackage currentPackage = packageManagerInternal.getPackage( - bp.perm.getPackageName()); + bp.mPermissionInfo.packageName); if (currentPackage == null) { currentOwnerIsSystem = false; } else { @@ -351,52 +369,52 @@ public final class BasePermission { } if (pkg.isSystem()) { - if (bp.type == BasePermission.TYPE_BUILTIN && bp.perm == null) { + if (bp.mType == BasePermission.TYPE_BUILTIN && bp.mPermissionInfo == null) { // It's a built-in permission and no owner, take ownership now - p.setFlags(p.getFlags() | PermissionInfo.FLAG_INSTALLED); - bp.perm = p; - bp.uid = pkg.getUid(); - bp.sourcePackageName = p.getPackageName(); + p.flags |= PermissionInfo.FLAG_INSTALLED; + bp.mPermissionInfo = p; + bp.mUid = pkg.getUid(); + bp.mPackageName = p.packageName; } else if (!currentOwnerIsSystem) { String msg = "New decl " + pkg + " of permission " - + p.getName() + " is system; overriding " + bp.sourcePackageName; + + p.name + " is system; overriding " + bp.mPackageName; PackageManagerService.reportSettingsProblem(Log.WARN, msg); bp = null; } } } if (bp == null) { - bp = new BasePermission(p.getName(), p.getPackageName(), TYPE_NORMAL); + bp = new BasePermission(p.name, p.packageName, TYPE_NORMAL); } StringBuilder r = null; - if (bp.perm == null) { - if (bp.sourcePackageName == null - || bp.sourcePackageName.equals(p.getPackageName())) { - final BasePermission tree = findPermissionTree(permissionTrees, p.getName()); + if (bp.mPermissionInfo == null) { + if (bp.mPackageName == null + || bp.mPackageName.equals(p.packageName)) { + final BasePermission tree = findPermissionTree(permissionTrees, p.name); if (tree == null - || tree.sourcePackageName.equals(p.getPackageName())) { - p.setFlags(p.getFlags() | PermissionInfo.FLAG_INSTALLED); - bp.perm = p; - bp.uid = pkg.getUid(); - bp.sourcePackageName = p.getPackageName(); + || tree.mPackageName.equals(p.packageName)) { + p.flags |= PermissionInfo.FLAG_INSTALLED; + bp.mPermissionInfo = p; + bp.mUid = pkg.getUid(); + bp.mPackageName = p.packageName; if (chatty) { if (r == null) { r = new StringBuilder(256); } else { r.append(' '); } - r.append(p.getName()); + r.append(p.name); } } else { - Slog.w(TAG, "Permission " + p.getName() + " from package " - + p.getPackageName() + " ignored: base tree " - + tree.name + " is from package " - + tree.sourcePackageName); + Slog.w(TAG, "Permission " + p.name + " from package " + + p.packageName + " ignored: base tree " + + tree.mName + " is from package " + + tree.mPackageName); } } else { - Slog.w(TAG, "Permission " + p.getName() + " from package " - + p.getPackageName() + " ignored: original from " - + bp.sourcePackageName); + Slog.w(TAG, "Permission " + p.name + " from package " + + p.packageName + " ignored: original from " + + bp.mPackageName); } } else if (chatty) { if (r == null) { @@ -405,11 +423,10 @@ public final class BasePermission { r.append(' '); } r.append("DUP:"); - r.append(p.getName()); + r.append(p.name); } - if (bp.perm != null && Objects.equals(bp.perm.getPackageName(), p.getPackageName()) - && Objects.equals(bp.perm.getName(), p.getName())) { - bp.protectionLevel = p.getProtectionLevel(); + if (bp.mPermissionInfo == p) { + bp.mProtectionLevel = p.protectionLevel; } if (PackageManagerService.DEBUG_PACKAGE_SCANNING && r != null) { Log.d(TAG, " Permissions: " + r); @@ -422,12 +439,12 @@ public final class BasePermission { if (permName != null) { BasePermission bp = findPermissionTree(permissionTrees, permName); if (bp != null) { - if (bp.uid == UserHandle.getAppId(callingUid)) { + if (bp.mUid == UserHandle.getAppId(callingUid)) { return bp; } throw new SecurityException("Calling uid " + callingUid + " is not allowed to add to permission tree " - + bp.name + " owned by uid " + bp.uid); + + bp.mName + " owned by uid " + bp.mUid); } } throw new SecurityException("No permission tree found for " + permName); @@ -436,45 +453,63 @@ public final class BasePermission { private static BasePermission findPermissionTree( Collection<BasePermission> permissionTrees, String permName) { for (BasePermission bp : permissionTrees) { - if (permName.startsWith(bp.name) && - permName.length() > bp.name.length() && - permName.charAt(bp.name.length()) == '.') { + if (permName.startsWith(bp.mName) + && permName.length() > bp.mName.length() + && permName.charAt(bp.mName.length()) == '.') { return bp; } } return null; } - public @Nullable PermissionInfo generatePermissionInfo(@NonNull String groupName, int flags) { - if (groupName == null) { - if (perm == null || perm.getGroup() == null) { - return generatePermissionInfo(protectionLevel, flags); - } - } else { - if (perm != null && groupName.equals(perm.getGroup())) { - return PackageInfoUtils.generatePermissionInfo(perm, flags); - } - } - return null; + @Nullable + public String getBackgroundPermission() { + return mPermissionInfo != null ? mPermissionInfo.backgroundPermission : null; + } + + @Nullable + public String getGroup() { + return mPermissionInfo != null ? mPermissionInfo.group : null; + } + + public int getProtection() { + return mProtectionLevel & PermissionInfo.PROTECTION_MASK_BASE; + } + + public int getProtectionFlags() { + return mProtectionLevel & PermissionInfo.PROTECTION_MASK_FLAGS; + } + + @NonNull + public PermissionInfo generatePermissionInfo(int flags) { + return generatePermissionInfo(flags, Build.VERSION_CODES.CUR_DEVELOPMENT); } - public @NonNull PermissionInfo generatePermissionInfo(int adjustedProtectionLevel, int flags) { + @NonNull + public PermissionInfo generatePermissionInfo(int flags, int targetSdkVersion) { PermissionInfo permissionInfo; - if (perm != null) { - final boolean protectionLevelChanged = protectionLevel != adjustedProtectionLevel; - permissionInfo = PackageInfoUtils.generatePermissionInfo(perm, flags); - if (protectionLevelChanged) { - // if we return different protection level, don't use the cached info - permissionInfo = new PermissionInfo(permissionInfo); - permissionInfo.protectionLevel = adjustedProtectionLevel; + if (mPermissionInfo != null) { + permissionInfo = new PermissionInfo(mPermissionInfo); + if ((flags & PackageManager.GET_META_DATA) != PackageManager.GET_META_DATA) { + permissionInfo.metaData = null; + } + } else { + permissionInfo = new PermissionInfo(); + permissionInfo.name = mName; + permissionInfo.packageName = mPackageName; + permissionInfo.nonLocalizedLabel = mName; + } + if (targetSdkVersion >= Build.VERSION_CODES.O) { + permissionInfo.protectionLevel = mProtectionLevel; + } else { + final int protection = mProtectionLevel & PermissionInfo.PROTECTION_MASK_BASE; + if (protection == PermissionInfo.PROTECTION_SIGNATURE) { + // Signature permission's protection flags are always reported. + permissionInfo.protectionLevel = mProtectionLevel; + } else { + permissionInfo.protectionLevel = protection; } - return permissionInfo; } - permissionInfo = new PermissionInfo(); - permissionInfo.name = name; - permissionInfo.packageName = sourcePackageName; - permissionInfo.nonLocalizedLabel = name; - permissionInfo.protectionLevel = protectionLevel; return permissionInfo; } @@ -496,23 +531,23 @@ public final class BasePermission { final boolean dynamic = "dynamic".equals(ptype); BasePermission bp = out.get(name); // If the permission is builtin, do not clobber it. - if (bp == null || bp.type != TYPE_BUILTIN) { + if (bp == null || bp.mType != TYPE_BUILTIN) { bp = new BasePermission(name.intern(), sourcePackage, dynamic ? TYPE_DYNAMIC : TYPE_NORMAL); } - bp.protectionLevel = readInt(parser, null, "protection", + bp.mProtectionLevel = readInt(parser, null, "protection", PermissionInfo.PROTECTION_NORMAL); - bp.protectionLevel = PermissionInfo.fixProtectionLevel(bp.protectionLevel); + bp.mProtectionLevel = PermissionInfo.fixProtectionLevel(bp.mProtectionLevel); if (dynamic) { final PermissionInfo pi = new PermissionInfo(); pi.packageName = sourcePackage.intern(); pi.name = name.intern(); pi.icon = readInt(parser, null, "icon", 0); pi.nonLocalizedLabel = parser.getAttributeValue(null, "label"); - pi.protectionLevel = bp.protectionLevel; - bp.pendingPermissionInfo = pi; + pi.protectionLevel = bp.mProtectionLevel; + bp.mPendingPermissionInfo = pi; } - out.put(bp.name, bp); + out.put(bp.mName, bp); return true; } @@ -533,22 +568,23 @@ public final class BasePermission { } public void writeLPr(@NonNull XmlSerializer serializer) throws IOException { - if (sourcePackageName == null) { + if (mPackageName == null) { return; } serializer.startTag(null, TAG_ITEM); - serializer.attribute(null, ATTR_NAME, name); - serializer.attribute(null, ATTR_PACKAGE, sourcePackageName); - if (protectionLevel != PermissionInfo.PROTECTION_NORMAL) { - serializer.attribute(null, "protection", Integer.toString(protectionLevel)); + serializer.attribute(null, ATTR_NAME, mName); + serializer.attribute(null, ATTR_PACKAGE, mPackageName); + if (mProtectionLevel != PermissionInfo.PROTECTION_NORMAL) { + serializer.attribute(null, "protection", Integer.toString(mProtectionLevel)); } - if (type == BasePermission.TYPE_DYNAMIC) { - if (perm != null || pendingPermissionInfo != null) { + if (mType == BasePermission.TYPE_DYNAMIC) { + if (mPermissionInfo != null || mPendingPermissionInfo != null) { serializer.attribute(null, "type", "dynamic"); - int icon = perm != null ? perm.getIcon() : pendingPermissionInfo.icon; - CharSequence nonLocalizedLabel = perm != null - ? perm.getNonLocalizedLabel() - : pendingPermissionInfo.nonLocalizedLabel; + int icon = mPermissionInfo != null ? mPermissionInfo.icon + : mPendingPermissionInfo.icon; + CharSequence nonLocalizedLabel = mPermissionInfo != null + ? mPermissionInfo.nonLocalizedLabel + : mPendingPermissionInfo.nonLocalizedLabel; if (icon != 0) { serializer.attribute(null, "icon", Integer.toString(icon)); @@ -561,27 +597,14 @@ public final class BasePermission { serializer.endTag(null, TAG_ITEM); } - private static boolean compareStrings(CharSequence s1, CharSequence s2) { - if (s1 == null) { - return s2 == null; - } - if (s2 == null) { - return false; - } - if (s1.getClass() != s2.getClass()) { - return false; - } - return s1.equals(s2); - } - - private static boolean comparePermissionInfos(ParsedPermission pi1, PermissionInfo pi2) { - if (pi1.getIcon() != pi2.icon) return false; - if (pi1.getLogo() != pi2.logo) return false; - if (pi1.getProtectionLevel() != pi2.protectionLevel) return false; - if (!compareStrings(pi1.getName(), pi2.name)) return false; - if (!compareStrings(pi1.getNonLocalizedLabel(), pi2.nonLocalizedLabel)) return false; + private static boolean comparePermissionInfos(PermissionInfo pi1, PermissionInfo pi2) { + if (pi1.icon != pi2.icon) return false; + if (pi1.logo != pi2.logo) return false; + if (pi1.protectionLevel != pi2.protectionLevel) return false; + if (!Objects.equals(pi1.name, pi2.name)) return false; + if (!Objects.equals(pi1.nonLocalizedLabel, pi2.nonLocalizedLabel)) return false; // We'll take care of setting this one. - if (!compareStrings(pi1.getPackageName(), pi2.packageName)) return false; + if (!Objects.equals(pi1.packageName, pi2.packageName)) return false; // These are not currently stored in settings. //if (!compareStrings(pi1.group, pi2.group)) return false; //if (!compareStrings(pi1.nonLocalizedDescription, pi2.nonLocalizedDescription)) return false; @@ -593,36 +616,34 @@ public final class BasePermission { public boolean dumpPermissionsLPr(@NonNull PrintWriter pw, @NonNull String packageName, @NonNull Set<String> permissionNames, boolean readEnforced, boolean printedSomething, @NonNull DumpState dumpState) { - if (packageName != null && !packageName.equals(sourcePackageName)) { + if (packageName != null && !packageName.equals(mPackageName)) { return false; } - if (permissionNames != null && !permissionNames.contains(name)) { + if (permissionNames != null && !permissionNames.contains(mName)) { return false; } if (!printedSomething) { if (dumpState.onTitlePrinted()) pw.println(); pw.println("Permissions:"); - printedSomething = true; } - pw.print(" Permission ["); pw.print(name); pw.print("] ("); + pw.print(" Permission ["); pw.print(mName); pw.print("] ("); pw.print(Integer.toHexString(System.identityHashCode(this))); pw.println("):"); - pw.print(" sourcePackage="); pw.println(sourcePackageName); - pw.print(" uid="); pw.print(uid); - pw.print(" gids="); pw.print(Arrays.toString( - computeGids(UserHandle.USER_SYSTEM))); - pw.print(" type="); pw.print(type); - pw.print(" prot="); - pw.println(PermissionInfo.protectionToString(protectionLevel)); - if (perm != null) { - pw.print(" perm="); pw.println(perm); - if ((perm.getFlags() & PermissionInfo.FLAG_INSTALLED) == 0 - || (perm.getFlags() & PermissionInfo.FLAG_REMOVED) != 0) { - pw.print(" flags=0x"); pw.println(Integer.toHexString(perm.getFlags())); + pw.print(" sourcePackage="); pw.println(mPackageName); + pw.print(" uid="); pw.print(mUid); + pw.print(" gids="); pw.print(Arrays.toString(computeGids(UserHandle.USER_SYSTEM))); + pw.print(" type="); pw.print(mType); + pw.print(" prot="); + pw.println(PermissionInfo.protectionToString(mProtectionLevel)); + if (mPermissionInfo != null) { + pw.print(" perm="); pw.println(mPermissionInfo); + if ((mPermissionInfo.flags & PermissionInfo.FLAG_INSTALLED) == 0 + || (mPermissionInfo.flags & PermissionInfo.FLAG_REMOVED) != 0) { + pw.print(" flags=0x"); pw.println(Integer.toHexString(mPermissionInfo.flags)); } } - if (READ_EXTERNAL_STORAGE.equals(name)) { + if (READ_EXTERNAL_STORAGE.equals(mName)) { pw.print(" enforced="); pw.println(readEnforced); } 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 25e1848816d1..d871325fe829 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java @@ -378,8 +378,9 @@ public class PermissionManagerService extends IPermissionManager.Stub { @NonNull Injector injector) { mInjector = injector; // The package info cache is the cache for package and permission information. + // Disable the package info and package permission caches locally but leave the + // checkPermission cache active. mInjector.invalidatePackageInfoCache(); - mInjector.disablePermissionCache(); mInjector.disablePackageNamePermissionCache(); mContext = context; @@ -544,24 +545,37 @@ public class PermissionManagerService extends IPermissionManager.Stub { @Override @Nullable - public PermissionInfo getPermissionInfo(String permName, String packageName, + public PermissionInfo getPermissionInfo(@NonNull String permName, @NonNull String opPackageName, @PermissionInfoFlags int flags) { final int callingUid = getCallingUid(); if (mPackageManagerInt.getInstantAppPackageName(callingUid) != null) { return null; } - final AndroidPackage pkg = mPackageManagerInt.getPackage(packageName); + final AndroidPackage opPackage = mPackageManagerInt.getPackage(opPackageName); + final int targetSdkVersion = getPermissionInfoCallingTargetSdkVersion(opPackage, + callingUid); synchronized (mLock) { final BasePermission bp = mSettings.getPermissionLocked(permName); if (bp == null) { return null; } - final int adjustedProtectionLevel = adjustPermissionProtectionFlagsLocked( - bp.getProtectionLevel(), pkg, callingUid); - return bp.generatePermissionInfo(adjustedProtectionLevel, flags); + return bp.generatePermissionInfo(flags, targetSdkVersion); } } + private int getPermissionInfoCallingTargetSdkVersion(@Nullable AndroidPackage pkg, int uid) { + final int appId = UserHandle.getAppId(uid); + if (appId == Process.ROOT_UID || appId == Process.SYSTEM_UID + || appId == Process.SHELL_UID) { + // System sees all flags. + return Build.VERSION_CODES.CUR_DEVELOPMENT; + } + if (pkg == null) { + return Build.VERSION_CODES.CUR_DEVELOPMENT; + } + return pkg.getTargetSdkVersion(); + } + @Override @Nullable public ParceledListSlice<PermissionInfo> queryPermissionsByGroup(String groupName, @@ -576,9 +590,8 @@ public class PermissionManagerService extends IPermissionManager.Stub { } final ArrayList<PermissionInfo> out = new ArrayList<PermissionInfo>(10); for (BasePermission bp : mSettings.mPermissions.values()) { - final PermissionInfo pi = bp.generatePermissionInfo(groupName, flags); - if (pi != null) { - out.add(pi); + if (Objects.equals(bp.getGroup(), groupName)) { + out.add(bp.generatePermissionInfo(flags)); } } return new ParceledListSlice<>(out); @@ -603,7 +616,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { int fixedLevel = PermissionInfo.fixProtectionLevel(info.protectionLevel); if (added) { enforcePermissionCapLocked(info, tree); - bp = new BasePermission(info.name, tree.getSourcePackageName(), + bp = new BasePermission(info.name, tree.getPackageName(), BasePermission.TYPE_DYNAMIC); } else if (!bp.isDynamic()) { throw new SecurityException("Not allowed to modify non-dynamic permission " @@ -2235,32 +2248,6 @@ public class PermissionManagerService extends IPermissionManager.Stub { } } - private int adjustPermissionProtectionFlagsLocked(int protectionLevel, - @Nullable AndroidPackage pkg, int uid) { - // Signature permission flags area always reported - final int protectionLevelMasked = protectionLevel - & (PermissionInfo.PROTECTION_NORMAL - | PermissionInfo.PROTECTION_DANGEROUS - | PermissionInfo.PROTECTION_SIGNATURE); - if (protectionLevelMasked == PermissionInfo.PROTECTION_SIGNATURE) { - return protectionLevel; - } - // System sees all flags. - final int appId = UserHandle.getAppId(uid); - if (appId == Process.SYSTEM_UID || appId == Process.ROOT_UID - || appId == Process.SHELL_UID) { - return protectionLevel; - } - if (pkg == null) { - return protectionLevel; - } - if (pkg.getTargetSdkVersion() < Build.VERSION_CODES.O) { - return protectionLevelMasked; - } - // Apps that target O see flags for all protection levels. - return protectionLevel; - } - /** * We might auto-grant permissions if any permission of the group is already granted. Hence if * the group of a granted permission changes we need to revoke it to avoid having permissions of @@ -2363,19 +2350,25 @@ public class PermissionManagerService extends IPermissionManager.Stub { } } + final PermissionInfo permissionInfo = PackageInfoUtils.generatePermissionInfo(p, + PackageManager.GET_META_DATA); + final BasePermission bp; if (p.isTree()) { - final BasePermission bp = BasePermission.createOrUpdate( + bp = BasePermission.createOrUpdate( mPackageManagerInt, - mSettings.getPermissionTreeLocked(p.getName()), p, pkg, + mSettings.getPermissionTreeLocked(p.getName()), permissionInfo, pkg, mSettings.getAllPermissionTreesLocked(), chatty); mSettings.putPermissionTreeLocked(p.getName(), bp); } else { - final BasePermission bp = BasePermission.createOrUpdate( + bp = BasePermission.createOrUpdate( mPackageManagerInt, mSettings.getPermissionLocked(p.getName()), - p, pkg, mSettings.getAllPermissionTreesLocked(), chatty); + permissionInfo, pkg, mSettings.getAllPermissionTreesLocked(), chatty); mSettings.putPermissionLocked(p.getName(), bp); } + if (bp.isInstalled()) { + p.setFlags(p.getFlags() | PermissionInfo.FLAG_INSTALLED); + } } } } @@ -2433,7 +2426,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { bp = mSettings.mPermissionTrees.get(p.getName()); } if (bp != null && bp.isPermission(p)) { - bp.setPermission(null); + bp.setPermissionInfo(null); if (DEBUG_REMOVE && chatty) { if (r == null) { r = new StringBuilder(256); @@ -2624,7 +2617,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { if (permission == null) { continue; } - if (Objects.equals(permission.getSourcePackageName(), PLATFORM_PACKAGE_NAME) + if (Objects.equals(permission.getPackageName(), PLATFORM_PACKAGE_NAME) && permission.isRuntime() && !permission.isRemoved()) { if (permission.isHardOrSoftRestricted() || permission.isImmutablyRestricted()) { @@ -2817,10 +2810,10 @@ public class PermissionManagerService extends IPermissionManager.Stub { boolean wasChanged = false; boolean restrictionExempt = - (origState.getPermissionFlags(bp.name) + (origState.getPermissionFlags(bp.getName()) & FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT) != 0; boolean restrictionApplied = (origState.getPermissionFlags( - bp.name) & FLAG_PERMISSION_APPLY_RESTRICTION) != 0; + bp.getName()) & FLAG_PERMISSION_APPLY_RESTRICTION) != 0; if (appSupportsRuntimePermissions) { // If hard restricted we don't allow holding it @@ -2868,7 +2861,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { if (origPermState == null) { // New permission if (PLATFORM_PACKAGE_NAME.equals( - bp.getSourcePackageName())) { + bp.getPackageName())) { if (!bp.isRemoved()) { flags |= FLAG_PERMISSION_REVIEW_REQUIRED | FLAG_PERMISSION_REVOKED_COMPAT; @@ -2877,7 +2870,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { } } - if (!uidState.isPermissionGranted(bp.name) + if (!uidState.isPermissionGranted(bp.getName()) && uidState.grantPermission(bp)) { wasChanged = true; } @@ -2914,7 +2907,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { flags); } else { if (DEBUG_PERMISSIONS) { - boolean wasGranted = uidState.isPermissionGranted(bp.name); + boolean wasGranted = uidState.isPermissionGranted(bp.getName()); if (wasGranted || bp.isAppOp()) { Slog.i(TAG, (wasGranted ? "Un-granting" : "Not granting") + " permission " + perm @@ -2926,7 +2919,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { + ")"); } } - if (uidState.removePermissionState(bp.name)) { + if (uidState.removePermissionState(bp.getName())) { changedInstallPermission = true; } } @@ -3309,7 +3302,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { final boolean isPrivilegedPermission = bp.isPrivileged() || isVendorPrivilegedPermission; final boolean isOemPermission = bp.isOEM(); if (!allowed && (isPrivilegedPermission || isOemPermission) && pkg.isSystem()) { - final String permissionName = bp.name; + final String permissionName = bp.getName(); // For updated system applications, a privileged/oem permission // is granted only if it had been defined by the original application. if (pkgSetting.getPkgState().isUpdatedSystemApp()) { @@ -3457,7 +3450,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { @Nullable private PackageSetting getSourcePackageSetting(@NonNull BasePermission bp) { - final String sourcePackageName = bp.getSourcePackageName(); + final String sourcePackageName = bp.getPackageName(); return mPackageManagerInt.getPackageSetting(sourcePackageName); } @@ -3467,14 +3460,14 @@ public class PermissionManagerService extends IPermissionManager.Stub { return false; } final boolean isPlatformPermission = PLATFORM_PACKAGE_NAME.equals( - permission.getSourcePackageName()); + permission.getPackageName()); if (!isPlatformPermission) { return true; } if (RoSystemProperties.CONTROL_PRIVAPP_PERMISSIONS_DISABLE) { return true; } - final String permissionName = permission.name; + final String permissionName = permission.getName(); if (isInSystemConfigPrivAppPermissions(pkg, permissionName)) { return true; } @@ -3861,7 +3854,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { // TODO(zhanghai): Why are we only killing the UID when GIDs changed, instead of any // permission change? - if (uidState.removePermissionState(bp.name) && bp.hasGids()) { + if (uidState.removePermissionState(bp.getName()) && bp.hasGids()) { affectedUserId = userId; } } @@ -3902,7 +3895,8 @@ public class PermissionManagerService extends IPermissionManager.Stub { if (!usedPermissions.contains(permissionState.getName())) { BasePermission bp = mSettings.getPermissionLocked(permissionState.getName()); if (bp != null) { - if (uidState.removePermissionState(bp.name) && permissionState.isRuntime()) { + if (uidState.removePermissionState(bp.getName()) + && permissionState.isRuntime()) { runtimePermissionChanged = true; } } @@ -3973,9 +3967,9 @@ public class PermissionManagerService extends IPermissionManager.Stub { // Only system declares background permissions, hence mapping does never change. mBackgroundPermissions = new ArrayMap<>(); for (BasePermission bp : mSettings.getAllPermissionsLocked()) { - if (bp.perm != null && bp.perm.getBackgroundPermission() != null) { - String fgPerm = bp.name; - String bgPerm = bp.perm.getBackgroundPermission(); + if (bp.getBackgroundPermission() != null) { + String fgPerm = bp.getName(); + String bgPerm = bp.getBackgroundPermission(); List<String> fgPerms = mBackgroundPermissions.get(bgPerm); if (fgPerms == null) { @@ -4124,7 +4118,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { if (bp.isDynamic()) { bp.updateDynamicPermission(mSettings.mPermissionTrees.values()); } - if (!packageName.equals(bp.getSourcePackageName())) { + if (!packageName.equals(bp.getPackageName())) { // Not checking sourcePackageSetting because it can be null when // the permission source package is the target package and the target package is // being uninstalled, @@ -4145,7 +4139,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { // From all other packages if (pkg == null || !hasPermission(pkg, bp.getName())) { Slog.i(TAG, "Removing permission " + bp.getName() - + " that used to be declared by " + bp.getSourcePackageName()); + + " that used to be declared by " + bp.getPackageName()); if (bp.isRuntime()) { final int[] userIds = mUserManagerInt.getUserIds(); final int numUserIds = userIds.length; @@ -4167,7 +4161,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { + p.getPackageName() + " and user " + userId); continue; } - uidState.removePermissionState(bp.name); + uidState.removePermissionState(bp.getName()); } } }); @@ -4176,16 +4170,16 @@ public class PermissionManagerService extends IPermissionManager.Stub { continue; } final AndroidPackage sourcePkg = - mPackageManagerInt.getPackage(bp.getSourcePackageName()); + mPackageManagerInt.getPackage(bp.getPackageName()); final PackageSetting sourcePs = (PackageSetting) mPackageManagerInt.getPackageSetting( - bp.getSourcePackageName()); + bp.getPackageName()); synchronized (mLock) { if (sourcePkg != null && sourcePs != null) { continue; } Slog.w(TAG, "Removing dangling permission: " + bp.getName() - + " from package " + bp.getSourcePackageName()); + + " from package " + bp.getPackageName()); mSettings.removePermissionLocked(bp.getName()); } } @@ -4257,7 +4251,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { final Iterator<BasePermission> it = mSettings.mPermissionTrees.values().iterator(); while (it.hasNext()) { final BasePermission bp = it.next(); - if (!packageName.equals(bp.getSourcePackageName())) { + if (!packageName.equals(bp.getPackageName())) { // Not checking sourcePackageSetting because it can be null when // the permission source package is the target package and the target package is // being uninstalled, @@ -4268,7 +4262,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { changed = true; if (pkg == null || !hasPermission(pkg, bp.getName())) { Slog.i(TAG, "Removing permission tree " + bp.getName() - + " that used to be declared by " + bp.getSourcePackageName()); + + " that used to be declared by " + bp.getPackageName()); it.remove(); } if (needsUpdate == null) { @@ -4280,16 +4274,16 @@ public class PermissionManagerService extends IPermissionManager.Stub { if (needsUpdate != null) { for (final BasePermission bp : needsUpdate) { final AndroidPackage sourcePkg = - mPackageManagerInt.getPackage(bp.getSourcePackageName()); + mPackageManagerInt.getPackage(bp.getPackageName()); final PackageSetting sourcePs = (PackageSetting) mPackageManagerInt.getPackageSetting( - bp.getSourcePackageName()); + bp.getPackageName()); synchronized (mLock) { if (sourcePkg != null && sourcePs != null) { continue; } Slog.w(TAG, "Removing dangling permission tree: " + bp.getName() - + " from package " + bp.getSourcePackageName()); + + " from package " + bp.getPackageName()); mSettings.removePermissionLocked(bp.getName()); } } @@ -4902,9 +4896,8 @@ public class PermissionManagerService extends IPermissionManager.Stub { for (int i = 0; i < numTotalPermissions; i++) { BasePermission bp = mSettings.mPermissions.valueAt(i); - if (bp.perm != null && bp.perm.getProtection() == protection) { - matchingPermissions.add( - PackageInfoUtils.generatePermissionInfo(bp.perm, 0)); + if (bp.getProtection() == protection) { + matchingPermissions.add(bp.generatePermissionInfo(0)); } } } @@ -4923,10 +4916,8 @@ public class PermissionManagerService extends IPermissionManager.Stub { for (int i = 0; i < numTotalPermissions; i++) { BasePermission bp = mSettings.mPermissions.valueAt(i); - if (bp.perm != null && (bp.perm.getProtectionFlags() & protectionFlags) - == protectionFlags) { - matchingPermissions.add( - PackageInfoUtils.generatePermissionInfo(bp.perm, 0)); + if ((bp.getProtectionFlags() & protectionFlags) == protectionFlags) { + matchingPermissions.add(bp.generatePermissionInfo(0)); } } } diff --git a/services/core/java/com/android/server/pm/permission/UidPermissionState.java b/services/core/java/com/android/server/pm/permission/UidPermissionState.java index 38e8955e8cdb..9727a5452440 100644 --- a/services/core/java/com/android/server/pm/permission/UidPermissionState.java +++ b/services/core/java/com/android/server/pm/permission/UidPermissionState.java @@ -284,7 +284,7 @@ public final class UidPermissionState { final PermissionState permissionState = getOrCreatePermissionState(permission); final boolean changed = permissionState.updateFlags(flagMask, flagValues); if (changed && permissionState.isDefault()) { - removePermissionState(permission.name); + removePermissionState(permission.getName()); } return changed; } diff --git a/services/core/java/com/android/server/policy/DisplayFoldController.java b/services/core/java/com/android/server/policy/DisplayFoldController.java index 0db3d78fed5b..f8e26fc57b95 100644 --- a/services/core/java/com/android/server/policy/DisplayFoldController.java +++ b/services/core/java/com/android/server/policy/DisplayFoldController.java @@ -83,32 +83,39 @@ class DisplayFoldController { if (mFolded != null && mFolded == folded) { return; } - if (folded) { - Rect foldedArea; - if (!mOverrideFoldedArea.isEmpty()) { - foldedArea = mOverrideFoldedArea; - } else if (!mFoldedArea.isEmpty()) { - foldedArea = mFoldedArea; - } else { - return; - } - mDisplayManagerInternal.getNonOverrideDisplayInfo(mDisplayId, mNonOverrideDisplayInfo); - final int dx = (mNonOverrideDisplayInfo.logicalWidth - foldedArea.width()) / 2 - - foldedArea.left; - final int dy = (mNonOverrideDisplayInfo.logicalHeight - foldedArea.height()) / 2 - - foldedArea.top; - - // Bypass scaling otherwise LogicalDisplay will scale contents by default. - mDisplayManagerInternal.setDisplayScalingDisabled(mDisplayId, true); - mWindowManagerInternal.setForcedDisplaySize(mDisplayId, - foldedArea.width(), foldedArea.height()); - mDisplayManagerInternal.setDisplayOffsets(mDisplayId, -dx, -dy); + final Rect foldedArea; + if (!mOverrideFoldedArea.isEmpty()) { + foldedArea = mOverrideFoldedArea; + } else if (!mFoldedArea.isEmpty()) { + foldedArea = mFoldedArea; } else { - mDisplayManagerInternal.setDisplayScalingDisabled(mDisplayId, false); - mWindowManagerInternal.clearForcedDisplaySize(mDisplayId); - mDisplayManagerInternal.setDisplayOffsets(mDisplayId, 0, 0); + foldedArea = null; } + + // Only do display scaling/cropping if it has been configured to do so + if (foldedArea != null) { + if (folded) { + + mDisplayManagerInternal.getNonOverrideDisplayInfo( + mDisplayId, mNonOverrideDisplayInfo); + final int dx = (mNonOverrideDisplayInfo.logicalWidth - foldedArea.width()) / 2 + - foldedArea.left; + final int dy = (mNonOverrideDisplayInfo.logicalHeight - foldedArea.height()) / 2 + - foldedArea.top; + + // Bypass scaling otherwise LogicalDisplay will scale contents by default. + mDisplayManagerInternal.setDisplayScalingDisabled(mDisplayId, true); + mWindowManagerInternal.setForcedDisplaySize(mDisplayId, + foldedArea.width(), foldedArea.height()); + mDisplayManagerInternal.setDisplayOffsets(mDisplayId, -dx, -dy); + } else { + mDisplayManagerInternal.setDisplayScalingDisabled(mDisplayId, false); + mWindowManagerInternal.clearForcedDisplaySize(mDisplayId); + mDisplayManagerInternal.setDisplayOffsets(mDisplayId, 0, 0); + } + } + mDurationLogger.setDeviceFolded(folded); mDurationLogger.logFocusedAppWithFoldState(folded, mFocusedApp); mFolded = folded; diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index df283e210be8..0983e10d2965 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -117,6 +117,7 @@ import android.database.ContentObserver; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.hardware.display.DisplayManager; +import android.hardware.display.DisplayManagerInternal; import android.hardware.hdmi.HdmiAudioSystemClient; import android.hardware.hdmi.HdmiControlManager; import android.hardware.hdmi.HdmiPlaybackClient; @@ -366,6 +367,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { StatusBarManagerInternal mStatusBarManagerInternal; AudioManagerInternal mAudioManagerInternal; DisplayManager mDisplayManager; + DisplayManagerInternal mDisplayManagerInternal; boolean mPreloadedRecentApps; final Object mServiceAquireLock = new Object(); Vibrator mVibrator; // Vibrator for giving feedback of orientation changes @@ -1752,6 +1754,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class); mAppOpsManager = mContext.getSystemService(AppOpsManager.class); mDisplayManager = mContext.getSystemService(DisplayManager.class); + mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class); mPackageManager = mContext.getPackageManager(); mHasFeatureWatch = mPackageManager.hasSystemFeature(FEATURE_WATCH); mHasFeatureLeanback = mPackageManager.hasSystemFeature(FEATURE_LEANBACK); @@ -4506,7 +4509,11 @@ public class PhoneWindowManager implements WindowManagerPolicy { // Called on the DisplayManager's DisplayPowerController thread. @Override - public void screenTurnedOff() { + public void screenTurnedOff(int displayId) { + if (displayId != DEFAULT_DISPLAY) { + return; + } + if (DEBUG_WAKEUP) Slog.i(TAG, "Screen turned off..."); updateScreenOffSleepToken(true); @@ -4529,7 +4536,11 @@ public class PhoneWindowManager implements WindowManagerPolicy { // Called on the DisplayManager's DisplayPowerController thread. @Override - public void screenTurningOn(final ScreenOnListener screenOnListener) { + public void screenTurningOn(int displayId, final ScreenOnListener screenOnListener) { + if (displayId != DEFAULT_DISPLAY) { + return; + } + if (DEBUG_WAKEUP) Slog.i(TAG, "Screen turning on..."); Trace.asyncTraceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "screenTurningOn", 0 /* cookie */); @@ -4552,7 +4563,11 @@ public class PhoneWindowManager implements WindowManagerPolicy { // Called on the DisplayManager's DisplayPowerController thread. @Override - public void screenTurnedOn() { + public void screenTurnedOn(int displayId) { + if (displayId != DEFAULT_DISPLAY) { + return; + } + synchronized (mLock) { if (mKeyguardDelegate != null) { mKeyguardDelegate.onScreenTurnedOn(); @@ -4562,7 +4577,11 @@ public class PhoneWindowManager implements WindowManagerPolicy { } @Override - public void screenTurningOff(ScreenOffListener screenOffListener) { + public void screenTurningOff(int displayId, ScreenOffListener screenOffListener) { + if (displayId != DEFAULT_DISPLAY) { + return; + } + mWindowManagerFuncs.screenTurningOff(screenOffListener); synchronized (mLock) { if (mKeyguardDelegate != null) { @@ -4824,8 +4843,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { } startedWakingUp(ON_BECAUSE_OF_UNKNOWN); finishedWakingUp(ON_BECAUSE_OF_UNKNOWN); - screenTurningOn(null); - screenTurnedOn(); + screenTurningOn(DEFAULT_DISPLAY, null); + screenTurnedOn(DEFAULT_DISPLAY); } @Override diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java index b96d65cb7433..0d8d3470ec65 100644 --- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java +++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java @@ -831,7 +831,7 @@ public interface WindowManagerPolicy extends WindowManagerPolicyConstants { public void finishedGoingToSleep(int why); /** - * Called when the device is about to turn on the screen to show content. + * Called when the display is about to turn on to show content. * When waking up, this method will be called once after the call to wakingUp(). * When dozing, the method will be called sometime after the call to goingToSleep() and * may be called repeatedly in the case where the screen is pulsing on and off. @@ -839,13 +839,13 @@ public interface WindowManagerPolicy extends WindowManagerPolicyConstants { * Must call back on the listener to tell it when the higher-level system * is ready for the screen to go on (i.e. the lock screen is shown). */ - public void screenTurningOn(ScreenOnListener screenOnListener); + public void screenTurningOn(int displayId, ScreenOnListener screenOnListener); /** - * Called when the device has actually turned on the screen, i.e. the display power state has - * been set to ON and the screen is unblocked. + * Called when the display has actually turned on, i.e. the display power state has been set to + * ON and the screen is unblocked. */ - public void screenTurnedOn(); + public void screenTurnedOn(int displayId); /** * Called when the display would like to be turned off. This gives policy a chance to do some @@ -854,12 +854,12 @@ public interface WindowManagerPolicy extends WindowManagerPolicyConstants { * @param screenOffListener Must be called to tell that the display power state can actually be * changed now after policy has done its work. */ - public void screenTurningOff(ScreenOffListener screenOffListener); + public void screenTurningOff(int displayId, ScreenOffListener screenOffListener); /** - * Called when the device has turned the screen off. + * Called when the display has turned off. */ - public void screenTurnedOff(); + public void screenTurnedOff(int displayId); public interface ScreenOnListener { void onScreenOn(); diff --git a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java index 9350edf6d68a..1e89e06617d3 100644 --- a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java +++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java @@ -28,7 +28,6 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.IntentSender; import android.content.pm.ApplicationInfo; -import android.content.pm.ModuleInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageInstaller; import android.content.pm.PackageManager; @@ -1089,8 +1088,8 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub implements Rollba Manifest.permission.TEST_MANAGE_ROLLBACKS, installerPackageName) == PackageManager.PERMISSION_GRANTED; - // For now only allow rollbacks for modules or for testing. - return (isRollbackWhitelisted(packageName) && manageRollbacksGranted) + // For now only allow rollbacks for allowlisted packages or for testing. + return (isRollbackAllowlisted(packageName) && manageRollbacksGranted) || testManageRollbacksGranted; } @@ -1098,25 +1097,8 @@ class RollbackManagerServiceImpl extends IRollbackManager.Stub implements Rollba * Returns true is this package is eligible for enabling rollback. */ @AnyThread - private boolean isRollbackWhitelisted(String packageName) { - // TODO: Remove #isModule when the allowlist is ready. - return SystemConfig.getInstance().getRollbackWhitelistedPackages().contains(packageName) - || isModule(packageName); - } - /** - * Returns true if the package name is the name of a module. - */ - @AnyThread - private boolean isModule(String packageName) { - PackageManager pm = mContext.getPackageManager(); - final ModuleInfo moduleInfo; - try { - moduleInfo = pm.getModuleInfo(packageName, 0); - } catch (PackageManager.NameNotFoundException e) { - return false; - } - - return moduleInfo != null; + private boolean isRollbackAllowlisted(String packageName) { + return SystemConfig.getInstance().getRollbackWhitelistedPackages().contains(packageName); } /** diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java index e0b671f38426..f43a4cec6080 100644 --- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java +++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java @@ -1630,13 +1630,14 @@ public class StatsPullAtomService extends SystemService { if (modemInfo == null) { return StatsManager.PULL_SKIP; } - pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag, modemInfo.getTimestamp(), + pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag, + modemInfo.getTimestampMillis(), modemInfo.getSleepTimeMillis(), modemInfo.getIdleTimeMillis(), - modemInfo.getTransmitPowerInfo().get(0).getTimeInMillis(), - modemInfo.getTransmitPowerInfo().get(1).getTimeInMillis(), - modemInfo.getTransmitPowerInfo().get(2).getTimeInMillis(), - modemInfo.getTransmitPowerInfo().get(3).getTimeInMillis(), - modemInfo.getTransmitPowerInfo().get(4).getTimeInMillis(), + modemInfo.getTransmitDurationMillisAtPowerLevel(0), + modemInfo.getTransmitDurationMillisAtPowerLevel(1), + modemInfo.getTransmitDurationMillisAtPowerLevel(2), + modemInfo.getTransmitDurationMillisAtPowerLevel(3), + modemInfo.getTransmitDurationMillisAtPowerLevel(4), modemInfo.getReceiveTimeMillis(), -1 /*`energy_used` field name deprecated, use -1 to indicate as unused.*/)); } finally { diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java index e9215f9793d2..ebfffec1774e 100644 --- a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java +++ b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java @@ -86,6 +86,13 @@ public interface StatusBarManagerInternal { void toggleSplitScreen(); void appTransitionFinished(int displayId); + /** + * Notifies the status bar that a Emergency Action launch gesture has been detected. + * + * TODO (b/169175022) Update method name and docs when feature name is locked. + */ + void onEmergencyActionLaunchGestureDetected(); + void toggleRecentApps(); void setCurrentUser(int newUserId); diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java index 6a68dc1fb2a2..55cb7f32e1b7 100644 --- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java +++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java @@ -264,6 +264,23 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D } } + /** + * Notifies the status bar that a Emergency Action launch gesture has been detected. + * + * TODO (b/169175022) Update method name and docs when feature name is locked. + */ + @Override + public void onEmergencyActionLaunchGestureDetected() { + if (SPEW) Slog.d(TAG, "Launching emergency action"); + if (mBar != null) { + try { + mBar.onEmergencyActionLaunchGestureDetected(); + } catch (RemoteException e) { + if (SPEW) Slog.d(TAG, "Failed to launch emergency action"); + } + } + } + @Override public void topAppWindowChanged(int displayId, boolean isFullscreen, boolean isImmersive) { StatusBarManagerService.this.topAppWindowChanged(displayId, isFullscreen, isImmersive); diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java index fb06a9cb5887..9d08b1be8d36 100644 --- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java +++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java @@ -397,6 +397,13 @@ class ActivityMetricsLogger { return -1; } } + + PackageOptimizationInfo getPackageOptimizationInfo(ArtManagerInternal artManagerInternal) { + return artManagerInternal == null || launchedActivityAppRecordRequiredAbi == null + ? PackageOptimizationInfo.createWithNoInfo() + : artManagerInternal.getPackageOptimizationInfo(applicationInfo, + launchedActivityAppRecordRequiredAbi, launchedActivityName); + } } ActivityMetricsLogger(ActivityStackSupervisor supervisor, Looper looper) { @@ -857,14 +864,8 @@ class ActivityMetricsLogger { info.bindApplicationDelayMs); } builder.addTaggedData(APP_TRANSITION_WINDOWS_DRAWN_DELAY_MS, info.windowsDrawnDelayMs); - final ArtManagerInternal artManagerInternal = getArtManagerInternal(); final PackageOptimizationInfo packageOptimizationInfo = - (artManagerInternal == null) || (info.launchedActivityAppRecordRequiredAbi == null) - ? PackageOptimizationInfo.createWithNoInfo() - : artManagerInternal.getPackageOptimizationInfo( - info.applicationInfo, - info.launchedActivityAppRecordRequiredAbi, - info.launchedActivityName); + info.getPackageOptimizationInfo(getArtManagerInternal()); builder.addTaggedData(PACKAGE_OPTIMIZATION_COMPILATION_REASON, packageOptimizationInfo.getCompilationReason()); builder.addTaggedData(PACKAGE_OPTIMIZATION_COMPILATION_FILTER, @@ -985,6 +986,8 @@ class ActivityMetricsLogger { builder.addTaggedData(APP_TRANSITION_PROCESS_RUNNING, info.mProcessRunning ? 1 : 0); mMetricsLogger.write(builder); + final PackageOptimizationInfo packageOptimizationInfo = + infoSnapshot.getPackageOptimizationInfo(getArtManagerInternal()); FrameworkStatsLog.write( FrameworkStatsLog.APP_START_FULLY_DRAWN, info.mLastLaunchedActivity.info.applicationInfo.uid, @@ -995,6 +998,8 @@ class ActivityMetricsLogger { info.mLastLaunchedActivity.info.name, info.mProcessRunning, startupTimeMs, + packageOptimizationInfo.getCompilationReason(), + packageOptimizationInfo.getCompilationFilter(), info.mSourceType, info.mSourceEventDelayMs); diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 8f2e60ec1f08..4e07d8e1f5a8 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -701,6 +701,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // Token for targeting this activity for assist purposes. final Binder assistToken = new Binder(); + // Tracking cookie for the launch of this activity and it's task. + IBinder mLaunchCookie; + private final Runnable mPauseTimeoutRunnable = new Runnable() { @Override public void run() { @@ -1643,6 +1646,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A mHandoverTaskDisplayArea = daToken != null ? (TaskDisplayArea) WindowContainer.fromBinder(daToken.asBinder()) : null; mHandoverLaunchDisplayId = options.getLaunchDisplayId(); + mLaunchCookie = options.getLaunchCookie(); } } @@ -3986,10 +3990,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } } - ActivityOptions getOptionsForTargetActivityLocked() { - return pendingOptions != null ? pendingOptions.forTargetActivity() : null; - } - void clearOptionsLocked() { clearOptionsLocked(true /* withAbort */); } diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index 48b0b7d27917..99b017f0277f 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -5572,7 +5572,10 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } void updateTopApp(ActivityRecord topResumedActivity) { - final ActivityRecord top = topResumedActivity != null ? topResumedActivity + // If system is sleeping, use the given record (it should be null) because there won't be + // the next resumed activity. Otherwise the process of pausing activity will keep with top + // state even the activity has paused and stopped. + final ActivityRecord top = mSleeping || topResumedActivity != null ? topResumedActivity // If there is no resumed activity, it will choose the pausing activity. : mRootWindowContainer.getTopResumedActivity(); mTopApp = top != null ? top.app : null; diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java index 0e47ea8058f1..6caf9162b7a8 100644 --- a/services/core/java/com/android/server/wm/AppTransition.java +++ b/services/core/java/com/android/server/wm/AppTransition.java @@ -21,7 +21,6 @@ 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_CRASHING_ACTIVITY_CLOSE; -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_SUBTLE_ANIMATION; import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE; @@ -92,7 +91,6 @@ import android.content.res.TypedArray; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Color; -import android.graphics.Path; import android.graphics.Picture; import android.graphics.Rect; import android.graphics.drawable.Drawable; @@ -134,7 +132,6 @@ import com.android.internal.util.function.pooled.PooledPredicate; import com.android.server.AttributeCache; import com.android.server.wm.animation.ClipRectLRAnimation; import com.android.server.wm.animation.ClipRectTBAnimation; -import com.android.server.wm.animation.CurvedTranslateAnimation; import java.io.PrintWriter; import java.util.ArrayList; @@ -161,9 +158,6 @@ public class AppTransition implements Dump { static final Interpolator TOUCH_RESPONSE_INTERPOLATOR = new PathInterpolator(0.3f, 0f, 0.1f, 1f); - private static final Interpolator THUMBNAIL_DOCK_INTERPOLATOR = - new PathInterpolator(0.85f, 0f, 1f, 1f); - /** * Maximum duration for the clip reveal animation. This is used when there is a lot of movement * involved, to make it more understandable. @@ -1127,11 +1121,8 @@ public class AppTransition implements Dump { scale.setInterpolator(interpolator); scale.setDuration(duration); Animation alpha = new AlphaAnimation(1f, 0f); - alpha.setInterpolator(mNextAppTransition == TRANSIT_DOCK_TASK_FROM_RECENTS - ? THUMBNAIL_DOCK_INTERPOLATOR : mThumbnailFadeOutInterpolator); - alpha.setDuration(mNextAppTransition == TRANSIT_DOCK_TASK_FROM_RECENTS - ? duration / 2 - : duration); + alpha.setInterpolator(mThumbnailFadeOutInterpolator); + alpha.setDuration(duration); Animation translate = createCurvedMotion(fromX, toX, fromY, toY); translate.setInterpolator(interpolator); translate.setDuration(duration); @@ -1194,44 +1185,15 @@ public class AppTransition implements Dump { } private Animation createCurvedMotion(float fromX, float toX, float fromY, float toY) { - - // Almost no x-change - use linear animation - if (Math.abs(toX - fromX) < 1f || mNextAppTransition != TRANSIT_DOCK_TASK_FROM_RECENTS) { - return new TranslateAnimation(fromX, toX, fromY, toY); - } else { - final Path path = createCurvedPath(fromX, toX, fromY, toY); - return new CurvedTranslateAnimation(path); - } - } - - private Path createCurvedPath(float fromX, float toX, float fromY, float toY) { - final Path path = new Path(); - path.moveTo(fromX, fromY); - - if (fromY > toY) { - // If the object needs to go up, move it in horizontal direction first, then vertical. - path.cubicTo(fromX, fromY, toX, 0.9f * fromY + 0.1f * toY, toX, toY); - } else { - // If the object needs to go down, move it in vertical direction first, then horizontal. - path.cubicTo(fromX, fromY, fromX, 0.1f * fromY + 0.9f * toY, toX, toY); - } - return path; + return new TranslateAnimation(fromX, toX, fromY, toY); } private long getAspectScaleDuration() { - if (mNextAppTransition == TRANSIT_DOCK_TASK_FROM_RECENTS) { - return (long) (THUMBNAIL_APP_TRANSITION_DURATION * 1.35f); - } else { - return THUMBNAIL_APP_TRANSITION_DURATION; - } + return THUMBNAIL_APP_TRANSITION_DURATION; } private Interpolator getAspectScaleInterpolator() { - if (mNextAppTransition == TRANSIT_DOCK_TASK_FROM_RECENTS) { - return mFastOutSlowInInterpolator; - } else { - return TOUCH_RESPONSE_INTERPOLATOR; - } + return TOUCH_RESPONSE_INTERPOLATOR; } /** @@ -1734,7 +1696,6 @@ public class AppTransition implements Dump { ? WindowAnimation_activityCloseEnterAnimation : WindowAnimation_activityCloseExitAnimation; break; - case TRANSIT_DOCK_TASK_FROM_RECENTS: case TRANSIT_TASK_OPEN: animAttr = enter ? WindowAnimation_taskOpenEnterAnimation @@ -1805,7 +1766,6 @@ public class AppTransition implements Dump { int getAppStackClipMode() { return mNextAppTransition == TRANSIT_ACTIVITY_RELAUNCH - || mNextAppTransition == TRANSIT_DOCK_TASK_FROM_RECENTS || mNextAppTransitionType == NEXT_TRANSIT_TYPE_CLIP_REVEAL || mNextAppTransition == TRANSIT_KEYGUARD_GOING_AWAY || mNextAppTransition == TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER @@ -2059,9 +2019,6 @@ public class AppTransition implements Dump { case TRANSIT_ACTIVITY_RELAUNCH: { return "TRANSIT_ACTIVITY_RELAUNCH"; } - case TRANSIT_DOCK_TASK_FROM_RECENTS: { - return "TRANSIT_DOCK_TASK_FROM_RECENTS"; - } case TRANSIT_KEYGUARD_GOING_AWAY: { return "TRANSIT_KEYGUARD_GOING_AWAY"; } diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java index 57d51c51c12b..6b8a3e22795e 100644 --- a/services/core/java/com/android/server/wm/AppTransitionController.java +++ b/services/core/java/com/android/server/wm/AppTransitionController.java @@ -21,7 +21,6 @@ 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_CRASHING_ACTIVITY_CLOSE; -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_SUBTLE_ANIMATION; import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_TO_SHADE; @@ -701,11 +700,9 @@ public class AppTransitionController { boolean openingAppHasWallpaper, boolean closingAppHasWallpaper) { // Given no app transition pass it through instead of a wallpaper transition. // Never convert the crashing transition. - // Never update the transition for the wallpaper if we are just docking from recents // Never convert a change transition since the top activity isn't changing and will likely // still be above an opening wallpaper. if (transit == TRANSIT_NONE || transit == TRANSIT_CRASHING_ACTIVITY_CLOSE - || transit == TRANSIT_DOCK_TASK_FROM_RECENTS || AppTransition.isChangeTransit(transit)) { return transit; } diff --git a/services/core/java/com/android/server/wm/ConfigurationContainer.java b/services/core/java/com/android/server/wm/ConfigurationContainer.java index 13033a6fcf81..60a62dc0b64c 100644 --- a/services/core/java/com/android/server/wm/ConfigurationContainer.java +++ b/services/core/java/com/android/server/wm/ConfigurationContainer.java @@ -157,6 +157,11 @@ public abstract class ConfigurationContainer<E extends ConfigurationContainer> { mResolvedOverrideConfiguration.setTo(mRequestedOverrideConfiguration); } + /** Returns {@code true} if requested override override configuration is not empty. */ + boolean hasRequestedOverrideConfiguration() { + return mHasOverrideConfiguration; + } + /** Returns requested override configuration applied to this configuration container. */ public Configuration getRequestedOverrideConfiguration() { return mRequestedOverrideConfiguration; diff --git a/services/core/java/com/android/server/wm/DisplayArea.java b/services/core/java/com/android/server/wm/DisplayArea.java index 1718b2aef0c9..38ad0706d949 100644 --- a/services/core/java/com/android/server/wm/DisplayArea.java +++ b/services/core/java/com/android/server/wm/DisplayArea.java @@ -41,6 +41,7 @@ import android.window.IDisplayAreaOrganizer; import com.android.internal.protolog.common.ProtoLog; import com.android.server.policy.WindowManagerPolicy; +import java.io.PrintWriter; import java.util.Comparator; import java.util.function.BiFunction; import java.util.function.Consumer; @@ -71,6 +72,13 @@ public class DisplayArea<T extends WindowContainer> extends WindowContainer<T> { IDisplayAreaOrganizer mOrganizer; private final Configuration mTmpConfiguration = new Configuration(); + /** + * Whether this {@link DisplayArea} should ignore fixed-orientation request. If {@code true}, it + * can never specify orientation, but shows the fixed-orientation apps below it in the + * letterbox; otherwise, it rotates based on the fixed-orientation request. + */ + protected boolean mIgnoreOrientationRequest; + DisplayArea(WindowManagerService wms, Type type, String name) { this(wms, type, name, FEATURE_UNDEFINED); } @@ -127,6 +135,56 @@ public class DisplayArea<T extends WindowContainer> extends WindowContainer<T> { } } + @Override + int getOrientation(int candidate) { + mLastOrientationSource = null; + if (mIgnoreOrientationRequest) { + return SCREEN_ORIENTATION_UNSET; + } + + return super.getOrientation(candidate); + } + + /** + * Sets whether this {@link DisplayArea} should ignore fixed-orientation request from apps and + * windows below it. + * + * @return Whether the display orientation changed after calling this method. + */ + boolean setIgnoreOrientationRequest(boolean ignoreOrientationRequest) { + if (mIgnoreOrientationRequest == ignoreOrientationRequest) { + return false; + } + mIgnoreOrientationRequest = ignoreOrientationRequest; + + // Check whether we should notify Display to update orientation. + if (mDisplayContent == null) { + return false; + } + + // The orientation request from this DA may now be respected. + if (!ignoreOrientationRequest) { + return mDisplayContent.updateOrientation(); + } + + final int lastOrientation = mDisplayContent.getLastOrientation(); + final WindowContainer lastOrientationSource = mDisplayContent.getLastOrientationSource(); + if (lastOrientation == SCREEN_ORIENTATION_UNSET + || lastOrientation == SCREEN_ORIENTATION_UNSPECIFIED) { + // Orientation won't be changed. + return false; + } + if (lastOrientationSource == null || lastOrientationSource.isDescendantOf(this)) { + // Try update if the orientation may be affected. + return mDisplayContent.updateOrientation(); + } + return false; + } + + boolean getIgnoreOrientationRequest() { + return mIgnoreOrientationRequest; + } + /** * When a {@link DisplayArea} is repositioned, it should only be moved among its siblings of the * same {@link Type}. @@ -200,6 +258,34 @@ public class DisplayArea<T extends WindowContainer> extends WindowContainer<T> { } @Override + void dump(PrintWriter pw, String prefix, boolean dumpAll) { + super.dump(pw, prefix, dumpAll); + if (mIgnoreOrientationRequest) { + pw.println(prefix + "mIgnoreOrientationRequest=true"); + } + if (hasRequestedOverrideConfiguration()) { + pw.println(prefix + "overrideConfig=" + getRequestedOverrideConfiguration()); + } + } + + void dumpChildDisplayArea(PrintWriter pw, String prefix, boolean dumpAll) { + final String doublePrefix = prefix + " "; + for (int i = getChildCount() - 1; i >= 0; i--) { + final DisplayArea<?> childArea = getChildAt(i).asDisplayArea(); + if (childArea == null) { + continue; + } + pw.println(prefix + "* " + childArea.getName()); + if (childArea.isTaskDisplayArea()) { + // TaskDisplayArea can only contain task. And it is already printed by display. + continue; + } + childArea.dump(pw, doublePrefix, dumpAll); + childArea.dumpChildDisplayArea(pw, doublePrefix, dumpAll); + } + } + + @Override long getProtoFieldId() { return DISPLAY_AREA; } @@ -409,6 +495,10 @@ public class DisplayArea<T extends WindowContainer> extends WindowContainer<T> { @Override int getOrientation(int candidate) { mLastOrientationSource = null; + if (mIgnoreOrientationRequest) { + return SCREEN_ORIENTATION_UNSET; + } + // Find a window requesting orientation. final WindowState win = getWindow(mGetOrientingWindow); diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index e7f0e3eb7a76..4a30ca96abb6 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -28,8 +28,8 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; -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.content.res.Configuration.ORIENTATION_LANDSCAPE; import static android.content.res.Configuration.ORIENTATION_PORTRAIT; import static android.content.res.Configuration.ORIENTATION_UNDEFINED; @@ -96,6 +96,7 @@ import static com.android.server.wm.DisplayContentProto.CURRENT_FOCUS; import static com.android.server.wm.DisplayContentProto.DISPLAY_FRAMES; import static com.android.server.wm.DisplayContentProto.DISPLAY_INFO; import static com.android.server.wm.DisplayContentProto.DISPLAY_READY; +import static com.android.server.wm.DisplayContentProto.DISPLAY_ROTATION; import static com.android.server.wm.DisplayContentProto.DPI; import static com.android.server.wm.DisplayContentProto.FOCUSED_APP; import static com.android.server.wm.DisplayContentProto.FOCUSED_ROOT_TASK_ID; @@ -107,7 +108,6 @@ import static com.android.server.wm.DisplayContentProto.INPUT_METHOD_TARGET; import static com.android.server.wm.DisplayContentProto.OPENING_APPS; import static com.android.server.wm.DisplayContentProto.RESUMED_ACTIVITY; import static com.android.server.wm.DisplayContentProto.ROOT_DISPLAY_AREA; -import static com.android.server.wm.DisplayContentProto.ROTATION; import static com.android.server.wm.DisplayContentProto.SCREEN_ROTATION_ANIMATION; import static com.android.server.wm.DisplayContentProto.SINGLE_TASK_INSTANCE; import static com.android.server.wm.Task.ActivityState.RESUMED; @@ -192,6 +192,8 @@ import android.view.IWindow; import android.view.InputChannel; import android.view.InputDevice; import android.view.InputWindowHandle; +import android.view.InsetsSource; +import android.view.InsetsState; import android.view.InsetsState.InternalInsetsType; import android.view.MagnificationSpec; import android.view.RemoteAnimationDefinition; @@ -2347,6 +2349,13 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp @Override int getOrientation() { mLastOrientationSource = null; + if (mIgnoreOrientationRequest) { + // Return SCREEN_ORIENTATION_UNSPECIFIED so that Display respect sensor rotation + ProtoLog.v(WM_DEBUG_ORIENTATION, + "Display id=%d is ignoring all orientation requests, return %d", + mDisplayId, SCREEN_ORIENTATION_UNSPECIFIED); + return SCREEN_ORIENTATION_UNSPECIFIED; + } if (mWmService.mDisplayFrozen) { if (mWmService.mPolicy.isKeyguardLocked()) { @@ -2363,19 +2372,15 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp } final int orientation = super.getOrientation(); - if (orientation != SCREEN_ORIENTATION_UNSET && orientation != SCREEN_ORIENTATION_BEHIND) { + if (orientation == SCREEN_ORIENTATION_UNSET) { + // Return SCREEN_ORIENTATION_UNSPECIFIED so that Display respect sensor rotation ProtoLog.v(WM_DEBUG_ORIENTATION, - "App is requesting an orientation, return %d for display id=%d", - orientation, mDisplayId); - return orientation; + "No app or window is requesting an orientation, return %d for display id=%d", + SCREEN_ORIENTATION_UNSPECIFIED, mDisplayId); + return SCREEN_ORIENTATION_UNSPECIFIED; } - ProtoLog.v(WM_DEBUG_ORIENTATION, - "No app is requesting an orientation, return %d for display id=%d", - getLastOrientation(), mDisplayId); - // The next app has not been requested to be visible, so we keep the current orientation - // to prevent freezing/unfreezing the display too early. - return getLastOrientation(); + return orientation; } void updateDisplayInfo() { @@ -2868,7 +2873,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp proto.write(ID, mDisplayId); proto.write(DPI, mBaseDisplayDensity); mDisplayInfo.dumpDebug(proto, DISPLAY_INFO); - proto.write(ROTATION, getRotation()); + mDisplayRotation.dumpDebug(proto, DISPLAY_ROTATION); final ScreenRotationAnimation screenRotationAnimation = getRotationAnimation(); if (screenRotationAnimation != null) { screenRotationAnimation.dumpDebug(proto, SCREEN_ROTATION_ANIMATION); @@ -2982,6 +2987,10 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp } pw.println(); + pw.println(prefix + "Display areas in top down Z order:"); + dumpChildDisplayArea(pw, subPrefix, dumpAll); + + pw.println(); pw.println(prefix + "Task display areas in top down Z order:"); forAllTaskDisplayAreas(taskDisplayArea -> { taskDisplayArea.dump(pw, prefix + " ", dumpAll); @@ -4243,6 +4252,10 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp @Override int getOrientation(int candidate) { + if (mIgnoreOrientationRequest) { + return SCREEN_ORIENTATION_UNSET; + } + // IME does not participate in orientation. return candidate; } @@ -4819,7 +4832,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp boolean ignoreRequest) { final int type = win.mAttrs.type; final boolean stickyHideNav = - !win.getRequestedInsetsState().getSourceOrDefaultVisibility(ITYPE_NAVIGATION_BAR) + !win.getRequestedVisibility(ITYPE_NAVIGATION_BAR) && win.mAttrs.insetsFlags.behavior == BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE; return (!stickyHideNav || ignoreRequest) && type != TYPE_INPUT_METHOD && type != TYPE_NOTIFICATION_SHADE && win.getActivityType() != ACTIVITY_TYPE_HOME; @@ -5349,8 +5362,13 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp return mDisplayPolicy.getSystemUiContext(); } - Point getDisplayPosition() { - return mWmService.mDisplayManagerInternal.getDisplayPosition(getDisplayId()); + @Override + boolean setIgnoreOrientationRequest(boolean ignoreOrientationRequest) { + if (mIgnoreOrientationRequest == ignoreOrientationRequest) return false; + final boolean rotationChanged = super.setIgnoreOrientationRequest(ignoreOrientationRequest); + mWmService.mDisplayWindowSettings.setIgnoreOrientationRequest( + this, mIgnoreOrientationRequest); + return rotationChanged; } /** @@ -5541,6 +5559,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp class RemoteInsetsControlTarget implements InsetsControlTarget { private final IDisplayWindowInsetsController mRemoteInsetsController; + private final InsetsState mRequestedInsetsState = new InsetsState(); RemoteInsetsControlTarget(IDisplayWindowInsetsController controller) { mRemoteInsetsController = controller; @@ -5596,6 +5615,19 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp Slog.w(TAG, "Failed to deliver showInsets", e); } } + + @Override + public boolean getRequestedVisibility(@InternalInsetsType int type) { + return mRequestedInsetsState.getSourceOrDefaultVisibility(type); + } + + void updateRequestedVisibility(InsetsState state) { + for (int i = 0; i < InsetsState.SIZE; i++) { + final InsetsSource source = state.peekSource(i); + if (source == null) continue; + mRequestedInsetsState.addSource(source); + } + } } /** diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java index 4e7e0ba99ff1..0801f68681ab 100644 --- a/services/core/java/com/android/server/wm/DisplayPolicy.java +++ b/services/core/java/com/android/server/wm/DisplayPolicy.java @@ -1659,10 +1659,8 @@ public class DisplayPolicy { provider != null ? provider.getControlTarget() : null; final WindowState navControllingWin = navControlTarget instanceof WindowState ? (WindowState) navControlTarget : null; - final InsetsState requestedState = navControllingWin != null - ? navControllingWin.getRequestedInsetsState() : null; - final boolean navVisible = requestedState != null - ? requestedState.getSourceOrDefaultVisibility(ITYPE_NAVIGATION_BAR) + final boolean navVisible = navControllingWin != null + ? navControllingWin.getRequestedVisibility(ITYPE_NAVIGATION_BAR) : InsetsState.getDefaultVisibility(ITYPE_NAVIGATION_BAR); final boolean showBarsByTouch = navControllingWin != null && navControllingWin.mAttrs.insetsFlags.behavior == BEHAVIOR_SHOW_BARS_BY_TOUCH; @@ -2073,11 +2071,9 @@ public class DisplayPolicy { // the cutout safe zone. if (cutoutMode != LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS) { final boolean attachedInParent = attached != null && !layoutInScreen; - final InsetsState requestedInsetsState = win.getRequestedInsetsState(); - final boolean requestedFullscreen = (fl & FLAG_FULLSCREEN) != 0 - || !requestedInsetsState.getSourceOrDefaultVisibility(ITYPE_STATUS_BAR); + final boolean requestedFullscreen = !win.getRequestedVisibility(ITYPE_STATUS_BAR); final boolean requestedHideNavigation = - !requestedInsetsState.getSourceOrDefaultVisibility(ITYPE_NAVIGATION_BAR); + !win.getRequestedVisibility(ITYPE_NAVIGATION_BAR); // TYPE_BASE_APPLICATION windows are never considered floating here because they don't // get cropped / shifted to the displayFrame in WindowState. @@ -2377,7 +2373,7 @@ public class DisplayPolicy { topIsFullscreen = topAppHidesStatusBar; // The subtle difference between the window for mTopFullscreenOpaqueWindowState // and mTopIsFullscreen is that mTopIsFullscreen is set only if the window - // has the FLAG_FULLSCREEN set. Not sure if there is another way that to be the + // requests to hide the status bar. Not sure if there is another way that to be the // case though. if (!topIsFullscreen || mDisplayContent.getDefaultTaskDisplayArea() .isStackVisible(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY)) { @@ -2421,16 +2417,7 @@ public class DisplayPolicy { if (mTopFullscreenOpaqueWindowState == null || mForceShowSystemBars) { return false; } - final LayoutParams attrs = mTopFullscreenOpaqueWindowState.getAttrs(); - final int fl = attrs.flags; - final InsetsSource request = mTopFullscreenOpaqueWindowState.getRequestedInsetsState() - .peekSource(ITYPE_STATUS_BAR); - if (WindowManagerDebugConfig.DEBUG) { - Slog.d(TAG, "frame: " + mTopFullscreenOpaqueWindowState.getFrame()); - Slog.d(TAG, "attr: " + attrs + " request: " + request); - } - return (fl & LayoutParams.FLAG_FULLSCREEN) != 0 - || (request != null && !request.isVisible()); + return !mTopFullscreenOpaqueWindowState.getRequestedVisibility(ITYPE_STATUS_BAR); } /** @@ -2864,18 +2851,15 @@ public class DisplayPolicy { return; } - final InsetsState requestedState = controlTarget.getRequestedInsetsState(); final @InsetsType int restorePositionTypes = - (requestedState.getSourceOrDefaultVisibility(ITYPE_NAVIGATION_BAR) + (controlTarget.getRequestedVisibility(ITYPE_NAVIGATION_BAR) ? Type.navigationBars() : 0) - | (requestedState.getSourceOrDefaultVisibility(ITYPE_STATUS_BAR) + | (controlTarget.getRequestedVisibility(ITYPE_STATUS_BAR) ? Type.statusBars() : 0) - | (mExtraNavBarAlt != null - && requestedState.getSourceOrDefaultVisibility( + | (mExtraNavBarAlt != null && controlTarget.getRequestedVisibility( ITYPE_EXTRA_NAVIGATION_BAR) ? Type.navigationBars() : 0) - | (mClimateBarAlt != null - && requestedState.getSourceOrDefaultVisibility( + | (mClimateBarAlt != null && controlTarget.getRequestedVisibility( ITYPE_CLIMATE_BAR) ? Type.statusBars() : 0); @@ -2981,12 +2965,11 @@ public class DisplayPolicy { win.mAttrs.insetsFlags.appearance, mTopFullscreenOpaqueWindowState, mTopFullscreenOpaqueOrDimmingWindowState, mDisplayContent.mInputMethodWindow, navColorWin) | opaqueAppearance; - final InsetsState requestedInsets = win.getRequestedInsetsState(); final int behavior = win.mAttrs.insetsFlags.behavior; final boolean isImmersive = behavior == BEHAVIOR_SHOW_BARS_BY_SWIPE || behavior == BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE; - final boolean isFullscreen = !requestedInsets.getSourceOrDefaultVisibility(ITYPE_STATUS_BAR) - || !requestedInsets.getSourceOrDefaultVisibility(ITYPE_NAVIGATION_BAR); + final boolean isFullscreen = !win.getRequestedVisibility(ITYPE_STATUS_BAR) + || !win.getRequestedVisibility(ITYPE_NAVIGATION_BAR); if (mLastDisableFlags == disableFlags && mLastAppearance == appearance && mLastFullscreenAppearance == fullscreenAppearance @@ -3152,10 +3135,7 @@ public class DisplayPolicy { freeformStackVisible, resizing, fullscreenDrawsNavBarBackground, dockedDrawsNavigationBarBackground); - final InsetsState requestedInsetsState = win.getRequestedInsetsState(); - final boolean requestHideNavBar = !requestedInsetsState.getSourceOrDefaultVisibility( - ITYPE_NAVIGATION_BAR); - + final boolean requestHideNavBar = !win.getRequestedVisibility(ITYPE_NAVIGATION_BAR); final long now = SystemClock.uptimeMillis(); final boolean pendingPanic = mPendingPanicGestureUptime != 0 && now - mPendingPanicGestureUptime <= PANIC_GESTURE_EXPIRATION; diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java index 0f43e49b568b..c5034316c101 100644 --- a/services/core/java/com/android/server/wm/DisplayRotation.java +++ b/services/core/java/com/android/server/wm/DisplayRotation.java @@ -24,6 +24,11 @@ import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLES import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ORIENTATION; import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.LID_OPEN; +import static com.android.server.wm.DisplayRotationProto.FIXED_TO_USER_ROTATION_MODE; +import static com.android.server.wm.DisplayRotationProto.FROZEN_TO_USER_ROTATION; +import static com.android.server.wm.DisplayRotationProto.LAST_ORIENTATION; +import static com.android.server.wm.DisplayRotationProto.ROTATION; +import static com.android.server.wm.DisplayRotationProto.USER_ROTATION; 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; @@ -51,6 +56,7 @@ import android.os.UserHandle; import android.provider.Settings; import android.util.Slog; import android.util.SparseArray; +import android.util.proto.ProtoOutputStream; import android.view.IDisplayWindowRotationCallback; import android.view.IWindowManager; import android.view.Surface; @@ -847,6 +853,10 @@ public class DisplayRotation { } } + int getFixedToUserRotationMode() { + return mFixedToUserRotation; + } + /** * Returns {@code true} if this display rotation takes app requested orientation into * consideration; {@code false} otherwise. For the time being the only case where this is {@code @@ -1453,6 +1463,16 @@ public class DisplayRotation { pw.println(prefix + " mFixedToUserRotation=" + isFixedToUserRotation()); } + void dumpDebug(ProtoOutputStream proto, long fieldId) { + final long token = proto.start(fieldId); + proto.write(ROTATION, getRotation()); + proto.write(FROZEN_TO_USER_ROTATION, isRotationFrozen()); + proto.write(USER_ROTATION, getUserRotation()); + proto.write(FIXED_TO_USER_ROTATION_MODE, mFixedToUserRotation); + proto.write(LAST_ORIENTATION, mLastOrientation); + proto.end(token); + } + private class OrientationListener extends WindowOrientationListener { final SparseArray<Runnable> mRunnableCache = new SparseArray<>(5); boolean mEnabled; diff --git a/services/core/java/com/android/server/wm/DisplayWindowSettings.java b/services/core/java/com/android/server/wm/DisplayWindowSettings.java index 04e37faf0ee4..f647bea950f2 100644 --- a/services/core/java/com/android/server/wm/DisplayWindowSettings.java +++ b/services/core/java/com/android/server/wm/DisplayWindowSettings.java @@ -112,6 +112,7 @@ class DisplayWindowSettings { private boolean mShouldShowSystemDecors = false; private boolean mShouldShowIme = false; private int mFixedToUserRotation = IWindowManager.FIXED_TO_USER_ROTATION_DEFAULT; + private boolean mIgnoreOrientationRequest = false; private Entry(String name) { mName = name; @@ -131,6 +132,7 @@ class DisplayWindowSettings { mShouldShowSystemDecors = copyFrom.mShouldShowSystemDecors; mShouldShowIme = copyFrom.mShouldShowIme; mFixedToUserRotation = copyFrom.mFixedToUserRotation; + mIgnoreOrientationRequest = copyFrom.mIgnoreOrientationRequest; } /** @return {@code true} if all values are default. */ @@ -144,7 +146,8 @@ class DisplayWindowSettings { && !mShouldShowWithInsecureKeyguard && !mShouldShowSystemDecors && !mShouldShowIme - && mFixedToUserRotation == IWindowManager.FIXED_TO_USER_ROTATION_DEFAULT; + && mFixedToUserRotation == IWindowManager.FIXED_TO_USER_ROTATION_DEFAULT + && !mIgnoreOrientationRequest; } } @@ -248,6 +251,15 @@ class DisplayWindowSettings { writeSettingsIfNeeded(entry, displayInfo); } + void setIgnoreOrientationRequest( + DisplayContent displayContent, boolean ignoreOrientationRequest) { + final DisplayInfo displayInfo = displayContent.getDisplayInfo(); + final Entry entry = getOrCreateEntry(displayInfo); + if (entry.mIgnoreOrientationRequest == ignoreOrientationRequest) return; + entry.mIgnoreOrientationRequest = ignoreOrientationRequest; + writeSettingsIfNeeded(entry, displayInfo); + } + private int getWindowingModeLocked(Entry entry, DisplayContent dc) { int windowingMode = entry != null ? entry.mWindowingMode : WindowConfiguration.WINDOWING_MODE_UNDEFINED; @@ -389,6 +401,7 @@ class DisplayWindowSettings { final boolean hasSizeOverride = entry.mForcedWidth != 0 && entry.mForcedHeight != 0; dc.mIsDensityForced = hasDensityOverride; dc.mIsSizeForced = hasSizeOverride; + dc.setIgnoreOrientationRequest(entry.mIgnoreOrientationRequest); final int width = hasSizeOverride ? entry.mForcedWidth : dc.mBaseDisplayWidth; final int height = hasSizeOverride ? entry.mForcedHeight : dc.mBaseDisplayHeight; @@ -529,6 +542,8 @@ class DisplayWindowSettings { entry.mShouldShowSystemDecors = getBooleanAttribute(parser, "shouldShowSystemDecors"); entry.mShouldShowIme = getBooleanAttribute(parser, "shouldShowIme"); entry.mFixedToUserRotation = getIntAttribute(parser, "fixedToUserRotation"); + entry.mIgnoreOrientationRequest + = getBooleanAttribute(parser, "ignoreOrientationRequest"); mEntries.put(name, entry); } XmlUtils.skipCurrentTag(parser); @@ -613,6 +628,10 @@ class DisplayWindowSettings { out.attribute(null, "fixedToUserRotation", Integer.toString(entry.mFixedToUserRotation)); } + if (entry.mIgnoreOrientationRequest) { + out.attribute(null, "ignoreOrientationRequest", + Boolean.toString(entry.mIgnoreOrientationRequest)); + } out.endTag(null, "display"); } diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java index 143b657b0437..470c2b1581d0 100644 --- a/services/core/java/com/android/server/wm/InputMonitor.java +++ b/services/core/java/com/android/server/wm/InputMonitor.java @@ -531,7 +531,7 @@ final class InputMonitor { // event. This is used to omit Surfaces from occlusion detection. populateOverlayInputInfo(mInvalidInputWindow, w.getName(), type, isVisible); mInputTransaction.setInputWindowInfo( - w.mWinAnimator.mSurfaceController.getClientViewRootSurface(), + w.mWinAnimator.mSurfaceController.mSurfaceControl, mInvalidInputWindow); return; } @@ -600,8 +600,7 @@ final class InputMonitor { if (w.mWinAnimator.hasSurface()) { mInputTransaction.setInputWindowInfo( - w.mWinAnimator.mSurfaceController.getClientViewRootSurface(), - inputWindowHandle); + w.mWinAnimator.mSurfaceController.mSurfaceControl, inputWindowHandle); } } } diff --git a/services/core/java/com/android/server/wm/InsetsControlTarget.java b/services/core/java/com/android/server/wm/InsetsControlTarget.java index 5e7ed3f80e43..287dd74e62c7 100644 --- a/services/core/java/com/android/server/wm/InsetsControlTarget.java +++ b/services/core/java/com/android/server/wm/InsetsControlTarget.java @@ -18,6 +18,7 @@ package com.android.server.wm; import android.inputmethodservice.InputMethodService; import android.view.InsetsState; +import android.view.InsetsState.InternalInsetsType; import android.view.WindowInsets.Type.InsetsType; /** @@ -39,10 +40,10 @@ interface InsetsControlTarget { } /** - * @return The requested {@link InsetsState} of this target. + * @return The requested visibility of this target. */ - default InsetsState getRequestedInsetsState() { - return InsetsState.EMPTY; + default boolean getRequestedVisibility(@InternalInsetsType int type) { + return InsetsState.getDefaultVisibility(type); } /** diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java index 361788497e11..bd05da9fe50a 100644 --- a/services/core/java/com/android/server/wm/InsetsPolicy.java +++ b/services/core/java/com/android/server/wm/InsetsPolicy.java @@ -173,13 +173,7 @@ class InsetsPolicy { // animation frame which will be triggered if a new leash is created. mDisplayContent.mWmService.mAnimator.getChoreographer().postFrameCallback(time -> { synchronized (mDisplayContent.mWmService.mGlobalLock) { - final InsetsState state = new InsetsState(mStateController.getRawInsetsState()); - startAnimation(true /* show */, () -> { - synchronized (mDisplayContent.mWmService.mGlobalLock) { - mStateController.notifyInsetsChanged(); - } - }, state); - mStateController.onInsetsModified(mDummyControlTarget, state); + startAnimation(true /* show */, null /* callback */); } }); } @@ -189,15 +183,18 @@ class InsetsPolicy { if (mShowingTransientTypes.size() == 0) { return; } - InsetsState state = new InsetsState(mStateController.getRawInsetsState()); startAnimation(false /* show */, () -> { synchronized (mDisplayContent.mWmService.mGlobalLock) { + for (int i = mShowingTransientTypes.size() - 1; i >= 0; i--) { + // We are about to clear mShowingTransientTypes, we don't want the transient bar + // can cause insets on the client. Restore the client visibility. + final @InternalInsetsType int type = mShowingTransientTypes.get(i); + mStateController.getSourceProvider(type).setClientVisible(false); + } mShowingTransientTypes.clear(); - mStateController.notifyInsetsChanged(); updateBarControlTarget(mFocusedWin); } - }, state); - mStateController.onInsetsModified(mDummyControlTarget, state); + }); } boolean isTransient(@InternalInsetsType int type) { @@ -211,7 +208,7 @@ class InsetsPolicy { final InsetsState originalState = mStateController.getInsetsForDispatch(target); InsetsState state = originalState; for (int i = mShowingTransientTypes.size() - 1; i >= 0; i--) { - final int type = mShowingTransientTypes.get(i); + final @InternalInsetsType int type = mShowingTransientTypes.get(i); final InsetsSource originalSource = state.peekSource(type); if (originalSource != null && originalSource.isVisible()) { if (state == originalState) { @@ -227,26 +224,25 @@ class InsetsPolicy { return state; } - void onInsetsModified(WindowState windowState, InsetsState state) { - mStateController.onInsetsModified(windowState, state); - checkAbortTransient(windowState, state); + void onInsetsModified(InsetsControlTarget caller) { + mStateController.onInsetsModified(caller); + checkAbortTransient(caller); updateBarControlTarget(mFocusedWin); } /** - * Called when a window modified the insets state. If the window set a insets source to visible - * while it is shown transiently, we need to abort the transient state. + * Called when a control target modified the insets state. If the target set a insets source to + * visible while it is shown transiently, we need to abort the transient state. * - * @param windowState who changed the insets state. - * @param state the modified insets state. + * @param caller who changed the insets state. */ - private void checkAbortTransient(WindowState windowState, InsetsState state) { + private void checkAbortTransient(InsetsControlTarget caller) { if (mShowingTransientTypes.size() != 0) { IntArray abortTypes = new IntArray(); for (int i = mShowingTransientTypes.size() - 1; i >= 0; i--) { - final int type = mShowingTransientTypes.get(i); - if (mStateController.isFakeTarget(type, windowState) - && state.getSource(type).isVisible()) { + final @InternalInsetsType int type = mShowingTransientTypes.get(i); + if (mStateController.isFakeTarget(type, caller) + && caller.getRequestedVisibility(type)) { mShowingTransientTypes.remove(i); abortTypes.add(type); } @@ -393,12 +389,12 @@ class InsetsPolicy { } @VisibleForTesting - void startAnimation(boolean show, Runnable callback, InsetsState state) { + void startAnimation(boolean show, Runnable callback) { int typesReady = 0; final SparseArray<InsetsSourceControl> controls = new SparseArray<>(); final IntArray showingTransientTypes = mShowingTransientTypes; for (int i = showingTransientTypes.size() - 1; i >= 0; i--) { - int type = showingTransientTypes.get(i); + final @InternalInsetsType int type = showingTransientTypes.get(i); InsetsSourceProvider provider = mStateController.getSourceProvider(type); InsetsSourceControl control = provider.getControl(mDummyControlTarget); if (control == null || control.getLeash() == null) { @@ -406,7 +402,6 @@ class InsetsPolicy { } typesReady |= InsetsState.toPublicType(type); controls.put(control.getType(), new InsetsSourceControl(control)); - state.setSourceVisible(type, show); } controlAnimationUnchecked(typesReady, controls, show, callback); } @@ -428,12 +423,9 @@ class InsetsPolicy { mId = id; } - private void updateVisibility(InsetsControlTarget controlTarget, + private void updateVisibility(@Nullable InsetsControlTarget controlTarget, @InternalInsetsType int type) { - final WindowState controllingWin = - controlTarget instanceof WindowState ? (WindowState) controlTarget : null; - setVisible(controllingWin == null - || controllingWin.getRequestedInsetsState().getSourceOrDefaultVisibility(type)); + setVisible(controlTarget == null || controlTarget.getRequestedVisibility(type)); } private void setVisible(boolean visible) { @@ -463,7 +455,9 @@ class InsetsPolicy { @Override protected void onAnimationFinish() { super.onAnimationFinish(); - DisplayThread.getHandler().post(mFinishCallback); + if (mFinishCallback != null) { + DisplayThread.getHandler().post(mFinishCallback); + } } private class InsetsPolicyAnimationControlCallbacks implements @@ -484,7 +478,7 @@ class InsetsPolicy { mAnimatingShown = show; mAnimationControl = new InsetsAnimationControlImpl(controls, - mFocusedWin.getDisplayContent().getBounds(), getState(), + mFocusedWin.getDisplayContent().getBounds(), mFocusedWin.getInsetsState(), mListener, typesReady, this, mListener.getDurationMs(), InsetsController.SYSTEM_BARS_INTERPOLATOR, show ? ANIMATION_TYPE_SHOW : ANIMATION_TYPE_HIDE); @@ -495,8 +489,7 @@ class InsetsPolicy { /** Called on SurfaceAnimationThread without global WM lock held. */ @Override public void scheduleApplyChangeInsets(InsetsAnimationControlRunner runner) { - InsetsState state = getState(); - if (mAnimationControl.applyChangeInsets(state)) { + if (mAnimationControl.applyChangeInsets(null /* outState */)) { mAnimationControl.finish(mAnimatingShown); } } @@ -507,20 +500,6 @@ class InsetsPolicy { // onAnimationFinished callback. } - /** - * This method will return a state with fullscreen frame override. No need to make copy - * after getting state from this method. - * @return The client insets state with full display frame override. - */ - private InsetsState getState() { - // To animate the transient animation correctly, we need to let the state hold - // the full display frame. - InsetsState overrideState = new InsetsState(mFocusedWin.getRequestedInsetsState(), - true); - overrideState.setDisplayFrame(mFocusedWin.getDisplayContent().getBounds()); - return overrideState; - } - /** Called on SurfaceAnimationThread without global WM lock held. */ @Override public void applySurfaceParams( diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java index 7defc5ddb3f4..e83151dae161 100644 --- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java +++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java @@ -338,11 +338,12 @@ class InsetsSourceProvider { } } - boolean onInsetsModified(InsetsControlTarget caller, InsetsSource modifiedSource) { - if (mControlTarget != caller || modifiedSource.isVisible() == mClientVisible) { + boolean updateClientVisibility(InsetsControlTarget caller) { + final boolean requestedVisible = caller.getRequestedVisibility(mSource.getType()); + if (caller != mControlTarget || requestedVisible == mClientVisible) { return false; } - setClientVisible(modifiedSource.isVisible()); + setClientVisible(requestedVisible); return true; } @@ -350,7 +351,7 @@ class InsetsSourceProvider { mIsLeashReadyForDispatching = true; } - private void setClientVisible(boolean clientVisible) { + void setClientVisible(boolean clientVisible) { if (mClientVisible == clientVisible) { return; } diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java index b59452ffaf36..b9c2093fe435 100644 --- a/services/core/java/com/android/server/wm/InsetsStateController.java +++ b/services/core/java/com/android/server/wm/InsetsStateController.java @@ -289,16 +289,10 @@ class InsetsStateController { winInsetsChanged.clear(); } - void onInsetsModified(InsetsControlTarget windowState, InsetsState state) { + void onInsetsModified(InsetsControlTarget caller) { boolean changed = false; - for (int i = 0; i < InsetsState.SIZE; i++) { - final InsetsSource source = state.peekSource(i); - if (source == null) continue; - final InsetsSourceProvider provider = mProviders.get(source.getType()); - if (provider == null) { - continue; - } - changed |= provider.onInsetsModified(windowState, source); + for (int i = mProviders.size() - 1; i >= 0; i--) { + changed |= mProviders.valueAt(i).updateClientVisibility(caller); } if (changed) { notifyInsetsChanged(); @@ -464,11 +458,24 @@ class InsetsStateController { final InsetsSourceProvider provider = mProviders.valueAt(i); provider.onSurfaceTransactionApplied(); } + final ArraySet<InsetsControlTarget> newControlTargets = new ArraySet<>(); for (int i = mPendingControlChanged.size() - 1; i >= 0; i--) { final InsetsControlTarget controlTarget = mPendingControlChanged.valueAt(i); controlTarget.notifyInsetsControlChanged(); + if (mControlTargetTypeMap.containsKey(controlTarget)) { + // We only collect targets who get controls, not lose controls. + newControlTargets.add(controlTarget); + } } mPendingControlChanged.clear(); + + // This updates the insets visibilities AFTER sending current insets state and controls + // to the clients, so that the clients can change the current visibilities to the + // requested visibilities with animations. + for (int i = newControlTargets.size() - 1; i >= 0; i--) { + onInsetsModified(newControlTargets.valueAt(i)); + } + newControlTargets.clear(); }); } diff --git a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java index 25732e7f0d99..7ed22a1f7777 100644 --- a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java +++ b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java @@ -203,16 +203,14 @@ class ScreenRotationAnimation { .setCallsite("ScreenRotationAnimation") .build(); - // In case display bounds change, screenshot buffer and surface may mismatch so set a - // scaling mode. - SurfaceControl.Transaction t2 = mService.mTransactionFactory.get(); - t2.setOverrideScalingMode(mScreenshotLayer, Surface.SCALING_MODE_SCALE_TO_WINDOW); - t2.apply(true /* sync */); - // Capture a screenshot into the surface we just created. final int displayId = displayContent.getDisplayId(); final Surface surface = mService.mSurfaceFactory.get(); + // In case display bounds change, screenshot buffer and surface may mismatch so set a + // scaling mode. surface.copyFrom(mScreenshotLayer); + surface.setScalingMode(Surface.SCALING_MODE_SCALE_TO_WINDOW); + SurfaceControl.ScreenshotHardwareBuffer screenshotBuffer = mService.mDisplayManagerInternal.systemScreenshot(displayId); if (screenshotBuffer != null) { diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java index 1fdb49f38cf5..1b887a7c9172 100644 --- a/services/core/java/com/android/server/wm/Session.java +++ b/services/core/java/com/android/server/wm/Session.java @@ -84,6 +84,9 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient { private final ArraySet<WindowSurfaceController> mAlertWindowSurfaces = new ArraySet<>(); private final DragDropController mDragDropController; final boolean mCanAddInternalSystemWindow; + // If non-system overlays from this process can be hidden by the user or app using + // HIDE_NON_SYSTEM_OVERLAY_WINDOWS. + final boolean mOverlaysCanBeHidden; final boolean mCanHideNonSystemOverlayWindows; final boolean mCanAcquireSleepToken; private AlertWindowNotification mAlertWindowNotification; @@ -92,6 +95,7 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient { private float mLastReportedAnimatorScale; private String mPackageName; private String mRelayoutTag; + private final InsetsState mDummyRequestedVisibility = new InsetsState(); private final InsetsSourceControl[] mDummyControls = new InsetsSourceControl[0]; public Session(WindowManagerService service, IWindowSessionCallback callback) { @@ -104,6 +108,8 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient { INTERNAL_SYSTEM_WINDOW) == PERMISSION_GRANTED; mCanHideNonSystemOverlayWindows = service.mContext.checkCallingOrSelfPermission( HIDE_NON_SYSTEM_OVERLAY_WINDOWS) == PERMISSION_GRANTED; + mOverlaysCanBeHidden = !mCanAddInternalSystemWindow + && !mService.mAtmInternal.isCallerRecents(mUid); mCanAcquireSleepToken = service.mContext.checkCallingOrSelfPermission(DEVICE_POWER) == PERMISSION_GRANTED; mShowingAlertWindowNotificationAllowed = mService.mShowAlertWindowNotifications; @@ -158,25 +164,26 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient { @Override public int addToDisplay(IWindow window, WindowManager.LayoutParams attrs, - int viewVisibility, int displayId, Rect outFrame, Rect outContentInsets, - Rect outStableInsets, + int viewVisibility, int displayId, InsetsState requestedVisibility, Rect outFrame, + Rect outContentInsets, Rect outStableInsets, DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel, InsetsState outInsetsState, InsetsSourceControl[] outActiveControls) { - return mService.addWindow(this, window, attrs, viewVisibility, displayId, outFrame, + return mService.addWindow(this, window, attrs, viewVisibility, displayId, + UserHandle.getUserId(mUid), requestedVisibility, outFrame, outContentInsets, outStableInsets, outDisplayCutout, outInputChannel, - outInsetsState, outActiveControls, UserHandle.getUserId(mUid)); + outInsetsState, outActiveControls); } @Override public int addToDisplayAsUser(IWindow window, WindowManager.LayoutParams attrs, - int viewVisibility, int displayId, int userId, Rect outFrame, - Rect outContentInsets, Rect outStableInsets, + int viewVisibility, int displayId, int userId, InsetsState requestedVisibility, + Rect outFrame, Rect outContentInsets, Rect outStableInsets, DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel, InsetsState outInsetsState, InsetsSourceControl[] outActiveControls) { - return mService.addWindow(this, window, attrs, viewVisibility, displayId, outFrame, - outContentInsets, outStableInsets, outDisplayCutout, outInputChannel, - outInsetsState, outActiveControls, userId); + return mService.addWindow(this, window, attrs, viewVisibility, displayId, userId, + requestedVisibility, outFrame, outContentInsets, outStableInsets, outDisplayCutout, + outInputChannel, outInsetsState, outActiveControls); } @Override @@ -184,9 +191,10 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient { int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets, InsetsState outInsetsState) { return mService.addWindow(this, window, attrs, viewVisibility, displayId, + UserHandle.getUserId(mUid), mDummyRequestedVisibility, new Rect() /* outFrame */, outContentInsets, outStableInsets, new DisplayCutout.ParcelableWrapper() /* cutout */, null /* outInputChannel */, - outInsetsState, mDummyControls, UserHandle.getUserId(mUid)); + outInsetsState, mDummyControls); } @Override @@ -204,15 +212,14 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient { int requestedWidth, int requestedHeight, int viewFlags, int flags, long frameNumber, ClientWindowFrames outFrames, MergedConfiguration mergedConfiguration, SurfaceControl outSurfaceControl, InsetsState outInsetsState, - InsetsSourceControl[] outActiveControls, Point outSurfaceSize, - SurfaceControl outBLASTSurfaceControl) { + InsetsSourceControl[] outActiveControls, Point outSurfaceSize) { if (false) Slog.d(TAG_WM, ">>>>>> ENTERED relayout from " + Binder.getCallingPid()); Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, mRelayoutTag); int res = mService.relayoutWindow(this, window, attrs, requestedWidth, requestedHeight, viewFlags, flags, frameNumber, outFrames, mergedConfiguration, outSurfaceControl, outInsetsState, - outActiveControls, outSurfaceSize, outBLASTSurfaceControl); + outActiveControls, outSurfaceSize); Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); if (false) Slog.d(TAG_WM, "<<<<<< EXITING relayout to " + Binder.getCallingPid()); @@ -494,9 +501,8 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient { final WindowState windowState = mService.windowForClientLocked(this, window, false /* throwOnError */); if (windowState != null) { - windowState.updateRequestedInsetsState(state); - windowState.getDisplayContent().getInsetsPolicy().onInsetsModified( - windowState, state); + windowState.updateRequestedVisibility(state); + windowState.getDisplayContent().getInsetsPolicy().onInsetsModified(windowState); } } } @@ -533,7 +539,7 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient { boolean changed; - if (!mCanAddInternalSystemWindow) { + if (mOverlaysCanBeHidden) { // We want to track non-system signature apps adding alert windows so we can post an // on-going notification for the user to control their visibility. if (visible) { diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index 249fe031b757..ecee46e895d0 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -1474,14 +1474,6 @@ class Task extends WindowContainer<WindowContainer> { // Update task bounds if needed. adjustBoundsForDisplayChangeIfNeeded(getDisplayContent()); - if (getWindowConfiguration().windowsAreScaleable()) { - // We force windows out of SCALING_MODE_FREEZE so that we can continue to animate them - // while a resize is pending. - forceWindowsScaleable(true /* force */); - } else { - forceWindowsScaleable(false /* force */); - } - mRootWindowContainer.updateUIDsPresentOnDisplay(); // Resume next focusable stack after reparenting to another display if we aren't removing @@ -3780,17 +3772,6 @@ class Task extends WindowContainer<WindowContainer> { positionChildAt(position, child, false /* includeParents */); } - void forceWindowsScaleable(boolean force) { - mWmService.openSurfaceTransaction(); - try { - for (int i = mChildren.size() - 1; i >= 0; i--) { - mChildren.get(i).forceWindowsScaleableInTransaction(force); - } - } finally { - mWmService.closeSurfaceTransaction("forceWindowsScaleable"); - } - } - void setTaskDescription(TaskDescription taskDescription) { mTaskDescription = taskDescription; } @@ -4062,6 +4043,9 @@ class Task extends WindowContainer<WindowContainer> { info.topActivityInfo = mReuseActivitiesReport.top != null ? mReuseActivitiesReport.top.info : null; + forAllActivities(r -> { + info.addLaunchCookie(r.mLaunchCookie); + }); } @Nullable PictureInPictureParams getPictureInPictureParams() { @@ -4792,9 +4776,11 @@ class Task extends WindowContainer<WindowContainer> { // If the task is not yet visible when it is added to the task organizer, then we should // hide it to allow the task organizer to show it when it is properly reparented. We // skip this for tasks created by the organizer because they can synchronously update - // the leash before new children are added to the task. + // the leash before new children are added to the task. Also skip this if the task + // has already been sent to the organizer which can happen before the first draw if + // an existing task is reported to the organizer when it first registers. if (!mAtmService.getTransitionController().isShellTransitionsEnabled() - && !mCreatedByOrganizer + && !mCreatedByOrganizer && !mTaskAppearedSent && mTaskOrganizer != null && !prevHasBeenVisible) { getSyncTransaction().hide(getSurfaceControl()); commitPendingTransaction(); @@ -4846,6 +4832,11 @@ class Task extends WindowContainer<WindowContainer> { @VisibleForTesting boolean setTaskOrganizer(ITaskOrganizer organizer) { + return setTaskOrganizer(organizer, false /* skipTaskAppeared */); + } + + @VisibleForTesting + boolean setTaskOrganizer(ITaskOrganizer organizer, boolean skipTaskAppeared) { if (mTaskOrganizer == organizer) { return false; } @@ -4858,7 +4849,9 @@ class Task extends WindowContainer<WindowContainer> { sendTaskVanished(prevOrganizer); if (mTaskOrganizer != null) { - sendTaskAppeared(); + if (!skipTaskAppeared) { + sendTaskAppeared(); + } } else { // No longer managed by any organizer. mTaskAppearedSent = false; @@ -4871,6 +4864,10 @@ class Task extends WindowContainer<WindowContainer> { return true; } + boolean updateTaskOrganizerState(boolean forceUpdate) { + return updateTaskOrganizerState(forceUpdate, false /* skipTaskAppeared */); + } + /** * Called when the task state changes (ie. from windowing mode change) an the task organizer * state should also be updated. @@ -4878,9 +4875,10 @@ class Task extends WindowContainer<WindowContainer> { * @param forceUpdate Updates the task organizer to the one currently specified in the task * org controller for the task's windowing mode, ignoring the cached * windowing mode checks. + * @param skipTaskAppeared Skips calling taskAppeared for the new organizer if it has changed * @return {@code true} if task organizer changed. */ - boolean updateTaskOrganizerState(boolean forceUpdate) { + boolean updateTaskOrganizerState(boolean forceUpdate, boolean skipTaskAppeared) { if (getSurfaceControl() == null) { // Can't call onTaskAppeared without a surfacecontrol, so defer this until after one // is created. @@ -4896,7 +4894,7 @@ class Task extends WindowContainer<WindowContainer> { if (!forceUpdate && mTaskOrganizer == organizer) { return false; } - return setTaskOrganizer(organizer); + return setTaskOrganizer(organizer, skipTaskAppeared); } @Override @@ -5883,7 +5881,11 @@ class Task extends WindowContainer<WindowContainer> { final TaskDisplayArea taskDisplayArea = getDisplayArea(); // If the top activity is the resumed one, nothing to do. + // For devices that are not in fullscreen mode (e.g. freeform windows), it's possible + // we still want to proceed if the visibility of other windows have changed (e.g. bringing + // a fullscreen window forward to cover another freeform activity.) if (mResumedActivity == next && next.isState(RESUMED) + && taskDisplayArea.getWindowingMode() != WINDOWING_MODE_FREEFORM && taskDisplayArea.allResumedActivitiesComplete()) { // Make sure we have executed any pending transitions, since there // should be nothing left to do at this point. diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java index d69fb0b823d0..830ad5d6043b 100644 --- a/services/core/java/com/android/server/wm/TaskDisplayArea.java +++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java @@ -29,10 +29,12 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMAR import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static android.app.WindowConfiguration.isSplitScreenWindowingMode; +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 com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ADD_REMOVE; +import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ORIENTATION; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_STATES; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_TASKS; import static com.android.server.wm.ActivityTaskManagerService.TAG_STACK; @@ -150,13 +152,6 @@ final class TaskDisplayArea extends DisplayArea<Task> { private boolean mRemoved; /** - * Whether the task display area should ignore fixed-orientation request. If {@code true}, it - * can never specify orientation, but show the fixed-orientation apps in the letterbox; - * otherwise, it rotates based on the fixed-orientation request when it has the focus. - */ - private boolean mIgnoreOrientationRequest; - - /** * The id of a leaf task that most recently being moved to front. */ private int mLastLeafTaskToFrontId; @@ -654,28 +649,9 @@ final class TaskDisplayArea extends DisplayArea<Task> { } } - /** - * Sets whether the task display area should ignore fixed-orientation request from apps. - * - * @return Whether the display orientation changed - */ - boolean setIgnoreOrientationRequest(boolean ignoreOrientationRequest) { - if (mIgnoreOrientationRequest == ignoreOrientationRequest) { - return false; - } - - mIgnoreOrientationRequest = ignoreOrientationRequest; - if (isLastFocused()) { - // Update orientation if this TDA is the last focused, otherwise it shouldn't affect - // the display. - return mDisplayContent.updateOrientation(); - } - - return false; - } - @Override int getOrientation(int candidate) { + mLastOrientationSource = null; // Only allow to specify orientation if this TDA is not set to ignore orientation request, // and it has the focus. if (mIgnoreOrientationRequest || !isLastFocused()) { @@ -708,7 +684,21 @@ final class TaskDisplayArea extends DisplayArea<Task> { return SCREEN_ORIENTATION_UNSPECIFIED; } - return super.getOrientation(candidate); + final int orientation = super.getOrientation(candidate); + if (orientation != SCREEN_ORIENTATION_UNSET + && orientation != SCREEN_ORIENTATION_BEHIND) { + ProtoLog.v(WM_DEBUG_ORIENTATION, + "App is requesting an orientation, return %d for display id=%d", + orientation, mDisplayContent.mDisplayId); + return orientation; + } + + ProtoLog.v(WM_DEBUG_ORIENTATION, + "No app is requesting an orientation, return %d for display id=%d", + mDisplayContent.getLastOrientation(), mDisplayContent.mDisplayId); + // The next app has not been requested to be visible, so we keep the current orientation + // to prevent freezing/unfreezing the display too early. + return mDisplayContent.getLastOrientation(); } @Override diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java index 8201d108c883..6486b78eb601 100644 --- a/services/core/java/com/android/server/wm/TaskOrganizerController.java +++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java @@ -32,6 +32,7 @@ import android.app.ActivityManager.TaskDescription; import android.app.WindowConfiguration; import android.content.Intent; import android.content.pm.ActivityInfo; +import android.content.pm.ParceledListSlice; import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; @@ -39,6 +40,7 @@ import android.util.Slog; import android.view.SurfaceControl; import android.window.ITaskOrganizer; import android.window.ITaskOrganizerController; +import android.window.TaskAppearedInfo; import android.window.WindowContainerToken; import com.android.internal.annotations.VisibleForTesting; @@ -76,8 +78,6 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { WINDOWING_MODE_FREEFORM }; - private final WindowManagerGlobalLock mGlobalLock; - private class DeathRecipient implements IBinder.DeathRecipient { ITaskOrganizer mTaskOrganizer; @@ -103,39 +103,38 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { * transaction before they are presented to the task org. */ private class TaskOrganizerCallbacks { - final WindowManagerService mService; final ITaskOrganizer mTaskOrganizer; final Consumer<Runnable> mDeferTaskOrgCallbacksConsumer; - private final SurfaceControl.Transaction mTransaction; - - TaskOrganizerCallbacks(WindowManagerService wm, ITaskOrganizer taskOrg, + TaskOrganizerCallbacks(ITaskOrganizer taskOrg, Consumer<Runnable> deferTaskOrgCallbacksConsumer) { - mService = wm; mDeferTaskOrgCallbacksConsumer = deferTaskOrgCallbacksConsumer; mTaskOrganizer = taskOrg; - mTransaction = wm.mTransactionFactory.get(); } IBinder getBinder() { return mTaskOrganizer.asBinder(); } + SurfaceControl prepareLeash(Task task, boolean visible, String reason) { + SurfaceControl outSurfaceControl = new SurfaceControl(task.getSurfaceControl(), reason); + if (!task.mCreatedByOrganizer && !visible) { + // To prevent flashes, we hide the task prior to sending the leash to the + // task org if the task has previously hidden (ie. when entering PIP) + mTransaction.hide(outSurfaceControl); + mTransaction.apply(); + } + return outSurfaceControl; + } + void onTaskAppeared(Task task) { ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Task appeared taskId=%d", task.mTaskId); final boolean visible = task.isVisible(); final RunningTaskInfo taskInfo = task.getTaskInfo(); mDeferTaskOrgCallbacksConsumer.accept(() -> { try { - SurfaceControl outSurfaceControl = new SurfaceControl(task.getSurfaceControl(), - "TaskOrganizerController.onTaskAppeared"); - if (!task.mCreatedByOrganizer && !visible) { - // To prevent flashes, we hide the task prior to sending the leash to the - // task org if the task has previously hidden (ie. when entering PIP) - mTransaction.hide(outSurfaceControl); - mTransaction.apply(); - } - mTaskOrganizer.onTaskAppeared(taskInfo, outSurfaceControl); + mTaskOrganizer.onTaskAppeared(taskInfo, prepareLeash(task, visible, + "TaskOrganizerController.onTaskAppeared")); } catch (RemoteException e) { Slog.e(TAG, "Exception sending onTaskAppeared callback", e); } @@ -162,17 +161,18 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { return; } ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Task info changed taskId=%d", task.mTaskId); - mDeferTaskOrgCallbacksConsumer.accept(() -> { - if (!task.isOrganized()) { - // This is safe to ignore if the task is no longer organized - return; - } - try { - mTaskOrganizer.onTaskInfoChanged(taskInfo); - } catch (RemoteException e) { - Slog.e(TAG, "Exception sending onTaskInfoChanged callback", e); - } - }); + if (!task.isOrganized()) { + // This is safe to ignore if the task is no longer organized + return; + } + try { + // Purposely notify of task info change immediately instead of deferring (like + // appear and vanish) to allow info changes (such as new PIP params) to flow + // without waiting. + mTaskOrganizer.onTaskInfoChanged(taskInfo); + } catch (RemoteException e) { + Slog.e(TAG, "Exception sending onTaskInfoChanged callback", e); + } } void onBackPressedOnTaskRoot(Task task) { @@ -183,17 +183,15 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { // by the organizer that don't receive that signal return; } - mDeferTaskOrgCallbacksConsumer.accept(() -> { - if (!task.isOrganized()) { - // This is safe to ignore if the task is no longer organized - return; - } - try { - mTaskOrganizer.onBackPressedOnTaskRoot(task.getTaskInfo()); - } catch (Exception e) { - Slog.e(TAG, "Exception sending onBackPressedOnTaskRoot callback", e); - } - }); + if (!task.isOrganized()) { + // This is safe to ignore if the task is no longer organized + return; + } + try { + mTaskOrganizer.onBackPressedOnTaskRoot(task.getTaskInfo()); + } catch (Exception e) { + Slog.e(TAG, "Exception sending onBackPressedOnTaskRoot callback", e); + } } } @@ -208,8 +206,7 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { mDeferTaskOrgCallbacksConsumer != null ? mDeferTaskOrgCallbacksConsumer : mService.mWindowManager.mAnimator::addAfterPrepareSurfacesRunnable; - mOrganizer = new TaskOrganizerCallbacks(mService.mWindowManager, organizer, - deferTaskOrgCallbacksConsumer); + mOrganizer = new TaskOrganizerCallbacks(organizer, deferTaskOrgCallbacksConsumer); mDeathRecipient = new DeathRecipient(organizer); try { organizer.asBinder().linkToDeath(mDeathRecipient, 0); @@ -219,6 +216,18 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { mUid = uid; } + /** + * Register this task with this state, but doesn't trigger the task appeared callback to + * the organizer. + */ + SurfaceControl addTaskWithoutCallback(Task t, String reason) { + t.mTaskAppearedSent = true; + if (!mOrganizedTasks.contains(t)) { + mOrganizedTasks.add(t); + } + return mOrganizer.prepareLeash(t, t.isVisible(), reason); + } + void addTask(Task t) { if (t.mTaskAppearedSent) return; @@ -265,6 +274,9 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { } } + private final ActivityTaskManagerService mService; + private final WindowManagerGlobalLock mGlobalLock; + // List of task organizers by priority private final LinkedList<ITaskOrganizer> mTaskOrganizers = new LinkedList<>(); private final HashMap<IBinder, TaskOrganizerState> mTaskOrganizerStates = new HashMap<>(); @@ -273,8 +285,7 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { // Set of organized tasks (by taskId) that dispatch back pressed to their organizers private final HashSet<Integer> mInterceptBackPressedOnRootTasks = new HashSet(); - private final ActivityTaskManagerService mService; - + private SurfaceControl.Transaction mTransaction; private RunningTaskInfo mTmpTaskInfo; private Consumer<Runnable> mDeferTaskOrgCallbacksConsumer; @@ -299,7 +310,7 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { * Register a TaskOrganizer to manage tasks as they enter the a supported windowing mode. */ @Override - public void registerTaskOrganizer(ITaskOrganizer organizer) { + public ParceledListSlice<TaskAppearedInfo> registerTaskOrganizer(ITaskOrganizer organizer) { enforceStackPermission("registerTaskOrganizer()"); final int uid = Binder.getCallingUid(); final long origId = Binder.clearCallingIdentity(); @@ -307,17 +318,36 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { synchronized (mGlobalLock) { ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Register task organizer=%s uid=%d", organizer.asBinder(), uid); + + // Defer initializing the transaction since the transaction factory can be set up + // by the tests after construction of the controller + if (mTransaction == null) { + mTransaction = mService.mWindowManager.mTransactionFactory.get(); + } + if (!mTaskOrganizerStates.containsKey(organizer.asBinder())) { mTaskOrganizers.add(organizer); mTaskOrganizerStates.put(organizer.asBinder(), new TaskOrganizerState(organizer, uid)); } + + final ArrayList<TaskAppearedInfo> taskInfos = new ArrayList<>(); + final TaskOrganizerState state = mTaskOrganizerStates.get(organizer.asBinder()); mService.mRootWindowContainer.forAllTasks((task) -> { if (ArrayUtils.contains(UNSUPPORTED_WINDOWING_MODES, task.getWindowingMode())) { return; } - task.updateTaskOrganizerState(true /* forceUpdate */); + + boolean returnTask = !task.mCreatedByOrganizer; + task.updateTaskOrganizerState(true /* forceUpdate */, + returnTask /* skipTaskAppeared */); + if (returnTask) { + SurfaceControl outSurfaceControl = state.addTaskWithoutCallback(task, + "TaskOrganizerController.registerTaskOrganizer"); + taskInfos.add(new TaskAppearedInfo(task.getTaskInfo(), outSurfaceControl)); + } }); + return new ParceledListSlice<>(taskInfos); } } finally { Binder.restoreCallingIdentity(origId); @@ -359,6 +389,10 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { } void onTaskAppeared(ITaskOrganizer organizer, Task task) { + // Don't send onTaskAppeared signal for task created by organizer since we will return it in + // the creation call. + if (task.mCreatedByOrganizer) return; + final TaskOrganizerState state = mTaskOrganizerStates.get(organizer.asBinder()); state.addTask(task); } @@ -371,7 +405,7 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { } @Override - public RunningTaskInfo createRootTask(int displayId, int windowingMode) { + public TaskAppearedInfo createRootTask(int displayId, int windowingMode) { enforceStackPermission("createRootTask()"); final long origId = Binder.clearCallingIdentity(); try { @@ -388,7 +422,11 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { true /* createdByOrganizer */); RunningTaskInfo out = task.getTaskInfo(); mLastSentTaskInfos.put(task, out); - return out; + final TaskOrganizerState state = + mTaskOrganizerStates.get(task.mTaskOrganizer.asBinder()); + final SurfaceControl outSurfaceControl = state.addTaskWithoutCallback(task, + "TaskOrganizerController.createRootTask"); + return new TaskAppearedInfo(task.getTaskInfo(), outSurfaceControl); } } finally { Binder.restoreCallingIdentity(origId); diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java index 87b470a54f77..01adb8b35c3b 100644 --- a/services/core/java/com/android/server/wm/TaskSnapshotController.java +++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java @@ -482,8 +482,7 @@ class TaskSnapshotController { final int color = ColorUtils.setAlphaComponent( task.getTaskDescription().getBackgroundColor(), 255); final LayoutParams attrs = mainWindow.getAttrs(); - final InsetsState insetsState = new InsetsState(mainWindow.getInsetsState()); - mergeInsetsSources(insetsState, mainWindow.getRequestedInsetsState()); + final InsetsState insetsState = getInsetsStateWithVisibilityOverride(mainWindow); final Rect systemBarInsets = getSystemBarInsets(mainWindow.getFrame(), insetsState); final SystemBarBackgroundPainter decorPainter = new SystemBarBackgroundPainter(attrs.flags, attrs.privateFlags, attrs.insetsFlags.appearance, task.getTaskDescription(), @@ -603,13 +602,18 @@ class TaskSnapshotController { return 0; } - static void mergeInsetsSources(InsetsState base, InsetsState other) { + static InsetsState getInsetsStateWithVisibilityOverride(WindowState win) { + final InsetsState state = new InsetsState(win.getInsetsState()); for (@InternalInsetsType int type = 0; type < InsetsState.SIZE; type++) { - final InsetsSource source = other.peekSource(type); - if (source != null) { - base.addSource(source); + final boolean requestedVisible = win.getRequestedVisibility(type); + InsetsSource source = state.peekSource(type); + if (source != null && source.isVisible() != requestedVisible) { + source = new InsetsSource(source); + source.setVisible(requestedVisible); + state.addSource(source); } } + return state; } static Rect getSystemBarInsets(Rect frame, InsetsState state) { diff --git a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java index 6904740343a6..e8c4491d3677 100644 --- a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java +++ b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java @@ -42,8 +42,8 @@ import static com.android.internal.policy.DecorView.NAVIGATION_BAR_COLOR_VIEW_AT import static com.android.internal.policy.DecorView.STATUS_BAR_COLOR_VIEW_ATTRIBUTES; import static com.android.internal.policy.DecorView.getNavigationBarRect; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_STARTING_WINDOW; +import static com.android.server.wm.TaskSnapshotController.getInsetsStateWithVisibilityOverride; import static com.android.server.wm.TaskSnapshotController.getSystemBarInsets; -import static com.android.server.wm.TaskSnapshotController.mergeInsetsSources; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; @@ -122,7 +122,6 @@ class TaskSnapshotSurface implements StartingSurface { //tmp vars for unused relayout params private static final Point sTmpSurfaceSize = new Point(); - private static final SurfaceControl sTmpSurfaceControl = new SurfaceControl(); private final Window mWindow; private final Surface mSurface; @@ -237,14 +236,15 @@ class TaskSnapshotSurface implements StartingSurface { task.getBounds(taskBounds); currentOrientation = topFullscreenOpaqueWindow.getConfiguration().orientation; activityType = activity.getActivityType(); - insetsState = new InsetsState(topFullscreenOpaqueWindow.getInsetsState()); - mergeInsetsSources(insetsState, topFullscreenOpaqueWindow.getRequestedInsetsState()); + insetsState = getInsetsStateWithVisibilityOverride(topFullscreenOpaqueWindow); + } try { final int res = session.addToDisplay(window, layoutParams, - View.GONE, activity.getDisplayContent().getDisplayId(), tmpFrames.frame, - tmpFrames.contentInsets, tmpFrames.stableInsets, tmpFrames.displayCutout, - null /* outInputChannel */, mTmpInsetsState, mTempControls); + View.GONE, activity.getDisplayContent().getDisplayId(), mTmpInsetsState, + tmpFrames.frame, tmpFrames.contentInsets, tmpFrames.stableInsets, + tmpFrames.displayCutout, null /* outInputChannel */, mTmpInsetsState, + mTempControls); if (res < 0) { Slog.w(TAG, "Failed to add snapshot starting window res=" + res); return null; @@ -260,7 +260,7 @@ class TaskSnapshotSurface implements StartingSurface { try { session.relayout(window, layoutParams, -1, -1, View.VISIBLE, 0, -1, tmpFrames, tmpMergedConfiguration, surfaceControl, mTmpInsetsState, - mTempControls, sTmpSurfaceSize, sTmpSurfaceControl); + mTempControls, sTmpSurfaceSize); } catch (RemoteException e) { // Local call. } diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java index 95d86621c541..0edaa1d821df 100644 --- a/services/core/java/com/android/server/wm/WindowContainer.java +++ b/services/core/java/com/android/server/wm/WindowContainer.java @@ -861,13 +861,6 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< } } - void forceWindowsScaleableInTransaction(boolean force) { - for (int i = mChildren.size() - 1; i >= 0; --i) { - final WindowContainer wc = mChildren.get(i); - wc.forceWindowsScaleableInTransaction(force); - } - } - /** * @return {@code true} when an application can override an app transition animation on this * container. diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 223aa1e9be39..ad624d50f7fd 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -122,10 +122,8 @@ import static com.android.server.wm.WindowManagerServiceDumpProto.FOCUSED_DISPLA import static com.android.server.wm.WindowManagerServiceDumpProto.FOCUSED_WINDOW; import static com.android.server.wm.WindowManagerServiceDumpProto.HARD_KEYBOARD_AVAILABLE; import static com.android.server.wm.WindowManagerServiceDumpProto.INPUT_METHOD_WINDOW; -import static com.android.server.wm.WindowManagerServiceDumpProto.LAST_ORIENTATION; import static com.android.server.wm.WindowManagerServiceDumpProto.POLICY; import static com.android.server.wm.WindowManagerServiceDumpProto.ROOT_WINDOW_CONTAINER; -import static com.android.server.wm.WindowManagerServiceDumpProto.ROTATION; import android.Manifest; import android.Manifest.permission; @@ -227,7 +225,7 @@ import android.view.IOnKeyguardExitResult; import android.view.IPinnedStackListener; import android.view.IRecentsAnimationRunner; import android.view.IRotationWatcher; -import android.view.IScrollCaptureController; +import android.view.IScrollCaptureCallbacks; import android.view.ISystemGestureExclusionListener; import android.view.IWallpaperVisibilityListener; import android.view.IWindow; @@ -1377,10 +1375,10 @@ public class WindowManagerService extends IWindowManager.Stub } public int addWindow(Session session, IWindow client, LayoutParams attrs, int viewVisibility, - int displayId, Rect outFrame, Rect outContentInsets, Rect outStableInsets, + int displayId, int requestUserId, InsetsState requestedVisibility, Rect outFrame, + Rect outContentInsets, Rect outStableInsets, DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel, - InsetsState outInsetsState, InsetsSourceControl[] outActiveControls, - int requestUserId) { + InsetsState outInsetsState, InsetsSourceControl[] outActiveControls) { Arrays.fill(outActiveControls, null); int[] appOp = new int[1]; final boolean isRoundedCornerOverlay = (attrs.privateFlags @@ -1575,6 +1573,7 @@ public class WindowManagerService extends IWindowManager.Stub final DisplayPolicy displayPolicy = displayContent.getDisplayPolicy(); displayPolicy.adjustWindowParamsLw(win, win.mAttrs, callingPid, callingUid); + win.updateRequestedVisibility(requestedVisibility); res = displayPolicy.validateAddingWindowLw(attrs, callingPid, callingUid); if (res != WindowManagerGlobal.ADD_OKAY) { @@ -2104,8 +2103,7 @@ public class WindowManagerService extends IWindowManager.Stub int requestedWidth, int requestedHeight, int viewVisibility, int flags, long frameNumber, ClientWindowFrames outFrames, MergedConfiguration mergedConfiguration, SurfaceControl outSurfaceControl, InsetsState outInsetsState, - InsetsSourceControl[] outActiveControls, Point outSurfaceSize, - SurfaceControl outBLASTSurfaceControl) { + InsetsSourceControl[] outActiveControls, Point outSurfaceSize) { Arrays.fill(outActiveControls, null); int result = 0; boolean configChanged; @@ -2280,8 +2278,7 @@ public class WindowManagerService extends IWindowManager.Stub result = win.relayoutVisibleWindow(result, attrChanges); try { - result = createSurfaceControl(outSurfaceControl, outBLASTSurfaceControl, - result, win, winAnimator); + result = createSurfaceControl(outSurfaceControl, result, win, winAnimator); } catch (Exception e) { displayContent.getInputMonitor().updateInputWindowsLw(true /*force*/); @@ -2313,7 +2310,6 @@ public class WindowManagerService extends IWindowManager.Stub // surface, let the client use that, but don't create new surface at this point. Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "relayoutWindow: getSurface"); winAnimator.mSurfaceController.getSurfaceControl(outSurfaceControl); - winAnimator.mSurfaceController.getBLASTSurfaceControl(outBLASTSurfaceControl); Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); } else { if (DEBUG_VISIBILITY) Slog.i(TAG_WM, "Releasing surface in: " + win); @@ -2501,8 +2497,7 @@ public class WindowManagerService extends IWindowManager.Stub return focusMayChange; } - private int createSurfaceControl(SurfaceControl outSurfaceControl, - SurfaceControl outBLASTSurfaceControl, int result, + private int createSurfaceControl(SurfaceControl outSurfaceControl, int result, WindowState win, WindowStateAnimator winAnimator) { if (!win.mHasSurface) { result |= RELAYOUT_RES_SURFACE_CHANGED; @@ -2517,7 +2512,6 @@ public class WindowManagerService extends IWindowManager.Stub } if (surfaceController != null) { surfaceController.getSurfaceControl(outSurfaceControl); - surfaceController.getBLASTSurfaceControl(outBLASTSurfaceControl); ProtoLog.i(WM_SHOW_TRANSACTIONS, "OUT SURFACE %s: copied", outSurfaceControl); } else { @@ -3714,16 +3708,63 @@ public class WindowManagerService extends IWindowManager.Stub @Override public void setFixedToUserRotation(int displayId, int fixedToUserRotation) { if (!checkCallingPermission(android.Manifest.permission.SET_ORIENTATION, - "freezeRotation()")) { + "setFixedToUserRotation()")) { throw new SecurityException("Requires SET_ORIENTATION permission"); } + final long origId = Binder.clearCallingIdentity(); + try { + synchronized (mGlobalLock) { + final DisplayContent display = mRoot.getDisplayContent(displayId); + if (display == null) { + Slog.w(TAG, "Trying to set fixed to user rotation for a missing display."); + return; + } + display.getDisplayRotation().setFixedToUserRotation(fixedToUserRotation); + } + } finally { + Binder.restoreCallingIdentity(origId); + } + } + + int getFixedToUserRotation(int displayId) { synchronized (mGlobalLock) { final DisplayContent display = mRoot.getDisplayContent(displayId); if (display == null) { - Slog.w(TAG, "Trying to set rotate for app for a missing display."); - return; + Slog.w(TAG, "Trying to get fixed to user rotation for a missing display."); + return -1; + } + return display.getDisplayRotation().getFixedToUserRotationMode(); + } + } + + @Override + public void setIgnoreOrientationRequest(int displayId, boolean ignoreOrientationRequest) { + mAtmInternal.enforceCallerIsRecentsOrHasPermission( + android.Manifest.permission.SET_ORIENTATION, "setIgnoreOrientationRequest()"); + + final long origId = Binder.clearCallingIdentity(); + try { + synchronized (mGlobalLock) { + final DisplayContent display = mRoot.getDisplayContent(displayId); + if (display == null) { + Slog.w(TAG, "Trying to setIgnoreOrientationRequest() for a missing display."); + return; + } + display.setIgnoreOrientationRequest(ignoreOrientationRequest); } - display.getDisplayRotation().setFixedToUserRotation(fixedToUserRotation); + } finally { + Binder.restoreCallingIdentity(origId); + } + } + + boolean getIgnoreOrientationRequest(int displayId) { + synchronized (mGlobalLock) { + final DisplayContent display = mRoot.getDisplayContent(displayId); + if (display == null) { + Slog.w(TAG, "Trying to getIgnoreOrientationRequest() for a missing display."); + return false; + } + return display.getIgnoreOrientationRequest(); } } @@ -3812,13 +3853,24 @@ public class WindowManagerService extends IWindowManager.Stub synchronized (mGlobalLock) { final DisplayContent display = mRoot.getDisplayContent(displayId); if (display == null) { - Slog.w(TAG, "Trying to thaw rotation for a missing display."); + Slog.w(TAG, "Trying to check if rotation is frozen on a missing display."); return false; } return display.getDisplayRotation().isRotationFrozen(); } } + int getDisplayUserRotation(int displayId) { + synchronized (mGlobalLock) { + final DisplayContent display = mRoot.getDisplayContent(displayId); + if (display == null) { + Slog.w(TAG, "Trying to get user rotation of a missing display."); + return -1; + } + return display.getDisplayRotation().getUserRotation(); + } + } + /** * Recalculate the current rotation. * @@ -3983,8 +4035,8 @@ public class WindowManagerService extends IWindowManager.Stub if (dc == null || dc.mRemoteInsetsControlTarget == null) { return; } - dc.getInsetsStateController().onInsetsModified( - dc.mRemoteInsetsControlTarget, state); + dc.mRemoteInsetsControlTarget.updateRequestedVisibility(state); + dc.getInsetsStateController().onInsetsModified(dc.mRemoteInsetsControlTarget); } } finally { Binder.restoreCallingIdentity(origId); @@ -5998,8 +6050,6 @@ public class WindowManagerService extends IWindowManager.Stub } proto.write(DISPLAY_FROZEN, mDisplayFrozen); final DisplayContent defaultDisplayContent = getDefaultDisplayContentLocked(); - proto.write(ROTATION, defaultDisplayContent.getRotation()); - proto.write(LAST_ORIENTATION, defaultDisplayContent.getLastOrientation()); proto.write(FOCUSED_DISPLAY_ID, topFocusedDisplayContent.getDisplayId()); proto.write(HARD_KEYBOARD_AVAILABLE, mHardKeyboardAvailable); } @@ -6852,16 +6902,17 @@ public class WindowManagerService extends IWindowManager.Stub * * @param displayId the display for the request * @param behindClient token for a window, used to filter the search to windows behind it - * @param taskId specifies the id of a task the result must belong to or -1 to ignore task ids - * @param controller the controller to receive results; a call to either - * {@link IScrollCaptureController#onClientConnected} or - * {@link IScrollCaptureController#onClientUnavailable}. + * @param taskId specifies the id of a task the result must belong to or -1 to match any task + * @param callbacks to receive responses */ public void requestScrollCapture(int displayId, @Nullable IBinder behindClient, int taskId, - IScrollCaptureController controller) { + IScrollCaptureCallbacks callbacks) { if (!checkCallingPermission(READ_FRAME_BUFFER, "requestScrollCapture()")) { throw new SecurityException("Requires READ_FRAME_BUFFER permission"); } + if (behindClient != null && !isWindowToken(behindClient)) { + throw new IllegalArgumentException("behindClient must be a window token"); + } final long token = Binder.clearCallingIdentity(); try { synchronized (mGlobalLock) { @@ -6869,26 +6920,26 @@ public class WindowManagerService extends IWindowManager.Stub if (dc == null) { ProtoLog.e(WM_ERROR, "Invalid displayId for requestScrollCapture: %d", displayId); - controller.onClientUnavailable(); + callbacks.onUnavailable(); return; } WindowState topWindow = null; if (behindClient != null) { - topWindow = windowForClientLocked(null, behindClient, /* throwOnError*/ true); + topWindow = windowForClientLocked(null, behindClient, /* throwOnError*/ false); } WindowState targetWindow = dc.findScrollCaptureTargetWindow(topWindow, taskId); if (targetWindow == null) { - controller.onClientUnavailable(); + callbacks.onUnavailable(); return; } // Forward to the window for handling. try { - targetWindow.mClient.requestScrollCapture(controller); + targetWindow.mClient.requestScrollCapture(callbacks); } catch (RemoteException e) { ProtoLog.w(WM_ERROR, "requestScrollCapture: caught exception dispatching to window." + "token=%s", targetWindow.mClient.asBinder()); - controller.onClientUnavailable(); + callbacks.onUnavailable(); } } } catch (RemoteException e) { diff --git a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java index 506e0dd508fb..3011f256bac6 100644 --- a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java +++ b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java @@ -28,7 +28,6 @@ import android.util.DisplayMetrics; import android.util.Pair; import android.view.Display; import android.view.IWindowManager; -import android.view.Surface; import android.view.ViewDebug; import com.android.internal.os.ByteTransferPipe; @@ -102,10 +101,14 @@ public class WindowManagerShellCommand extends ShellCommand { } } return result; - case "set-user-rotation": - return runSetDisplayUserRotation(pw); - case "set-fix-to-user-rotation": - return runSetFixToUserRotation(pw); + case "user-rotation": + return runDisplayUserRotation(pw); + case "fixed-to-user-rotation": + return runFixedToUserRotation(pw); + case "set-ignore-orientation-request": + return runSetIgnoreOrientationRequest(pw); + case "get-ignore-orientation-request": + return runGetIgnoreOrientationRequest(pw); case "dump-visible-window-views": return runDumpVisibleWindowViews(pw); default: @@ -309,28 +312,37 @@ public class WindowManagerShellCommand extends ShellCommand { return Integer.parseInt(s); } - private int runSetDisplayUserRotation(PrintWriter pw) { - final String lockMode = getNextArgRequired(); - + private int runDisplayUserRotation(PrintWriter pw) { int displayId = Display.DEFAULT_DISPLAY; String arg = getNextArg(); + if (arg == null) { + return printDisplayUserRotation(pw, displayId); + } + if ("-d".equals(arg)) { displayId = Integer.parseInt(getNextArgRequired()); arg = getNextArg(); } + final String lockMode = arg; + if (lockMode == null) { + return printDisplayUserRotation(pw, displayId); + } + if ("free".equals(lockMode)) { mInternal.thawDisplayRotation(displayId); return 0; } - if (!lockMode.equals("lock")) { - getErrPrintWriter().println("Error: lock mode needs to be either free or lock."); + if (!"lock".equals(lockMode)) { + getErrPrintWriter().println("Error: argument needs to be either -d, free or lock."); return -1; } + arg = getNextArg(); try { - final int rotation = arg != null ? Integer.parseInt(arg) : Surface.ROTATION_0; + final int rotation = + arg != null ? Integer.parseInt(arg) : -1 /* lock to current rotation */; mInternal.freezeDisplayRotation(displayId, rotation); return 0; } catch (IllegalArgumentException e) { @@ -339,12 +351,36 @@ public class WindowManagerShellCommand extends ShellCommand { } } - private int runSetFixToUserRotation(PrintWriter pw) throws RemoteException { + private int printDisplayUserRotation(PrintWriter pw, int displayId) { + final int displayUserRotation = mInternal.getDisplayUserRotation(displayId); + if (displayUserRotation < 0) { + getErrPrintWriter().println("Error: check logcat for more details."); + return -1; + } + if (!mInternal.isDisplayRotationFrozen(displayId)) { + pw.println("free"); + return 0; + } + pw.print("lock "); + pw.println(displayUserRotation); + return 0; + } + + private int runFixedToUserRotation(PrintWriter pw) throws RemoteException { int displayId = Display.DEFAULT_DISPLAY; - String arg = getNextArgRequired(); + String arg = getNextArg(); + if (arg == null) { + printFixedToUserRotation(pw, displayId); + return 0; + } + if ("-d".equals(arg)) { displayId = Integer.parseInt(getNextArgRequired()); - arg = getNextArgRequired(); + arg = getNextArg(); + } + + if (arg == null) { + return printFixedToUserRotation(pw, displayId); } final int fixedToUserRotation; @@ -368,6 +404,65 @@ public class WindowManagerShellCommand extends ShellCommand { return 0; } + private int printFixedToUserRotation(PrintWriter pw, int displayId) { + int fixedToUserRotationMode = mInternal.getFixedToUserRotation(displayId); + switch (fixedToUserRotationMode) { + case IWindowManager.FIXED_TO_USER_ROTATION_DEFAULT: + pw.println("default"); + return 0; + case IWindowManager.FIXED_TO_USER_ROTATION_DISABLED: + pw.println("disabled"); + return 0; + case IWindowManager.FIXED_TO_USER_ROTATION_ENABLED: + pw.println("enabled"); + return 0; + default: + getErrPrintWriter().println("Error: check logcat for more details."); + return -1; + } + } + + private int runSetIgnoreOrientationRequest(PrintWriter pw) throws RemoteException { + int displayId = Display.DEFAULT_DISPLAY; + String arg = getNextArgRequired(); + if ("-d".equals(arg)) { + displayId = Integer.parseInt(getNextArgRequired()); + arg = getNextArgRequired(); + } + + final boolean ignoreOrientationRequest; + switch (arg) { + case "true": + case "1": + ignoreOrientationRequest = true; + break; + case "false": + case "0": + ignoreOrientationRequest = false; + break; + default: + getErrPrintWriter().println("Error: expecting true, 1, false, 0, but we " + + "get " + arg); + return -1; + } + + mInterface.setIgnoreOrientationRequest(displayId, ignoreOrientationRequest); + return 0; + } + + private int runGetIgnoreOrientationRequest(PrintWriter pw) throws RemoteException { + int displayId = Display.DEFAULT_DISPLAY; + String arg = getNextArg(); + if ("-d".equals(arg)) { + displayId = Integer.parseInt(getNextArgRequired()); + } + + final boolean ignoreOrientationRequest = mInternal.getIgnoreOrientationRequest(displayId); + pw.println("ignoreOrientationRequest " + ignoreOrientationRequest + + " for displayId=" + displayId); + return 0; + } + private int runDumpVisibleWindowViews(PrintWriter pw) { if (!mInternal.checkCallingPermission(android.Manifest.permission.DUMP, "runDumpVisibleWindowViews()")) { @@ -429,12 +524,15 @@ public class WindowManagerShellCommand extends ShellCommand { pw.println(" Set display scaling mode."); pw.println(" dismiss-keyguard"); pw.println(" Dismiss the keyguard, prompting user for auth if necessary."); - pw.println(" set-user-rotation [free|lock] [-d DISPLAY_ID] [rotation]"); - pw.println(" Set user rotation mode and user rotation."); + pw.println(" user-rotation [-d DISPLAY_ID] [free|lock] [rotation]"); + pw.println(" Print or set user rotation mode and user rotation."); pw.println(" dump-visible-window-views"); pw.println(" Dumps the encoded view hierarchies of visible windows"); - pw.println(" set-fix-to-user-rotation [-d DISPLAY_ID] [enabled|disabled]"); - pw.println(" Enable or disable rotating display for app requested orientation."); + pw.println(" fixed-to-user-rotation [-d DISPLAY_ID] [enabled|disabled|default]"); + pw.println(" Print or set rotating display for app requested orientation."); + pw.println(" set-ignore-orientation-request [-d DISPLAY_ID] [true|1|false|0]"); + pw.println(" get-ignore-orientation-request [-d DISPLAY_ID] "); + pw.println(" If app requested orientation should be ignored."); if (!IS_USER) { pw.println(" tracing (start | stop)"); pw.println(" Start or stop window tracing."); diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java index 6a5784435ad6..5e07f5187c54 100644 --- a/services/core/java/com/android/server/wm/WindowOrganizerController.java +++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java @@ -380,24 +380,18 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub return effects; } - private int applyTaskDisplayAreaChanges(TaskDisplayArea taskDisplayArea, + private int applyDisplayAreaChanges(DisplayArea displayArea, WindowContainerTransaction.Change c) { - int effects = applyDisplayAreaChanges(taskDisplayArea, c); + final int[] effects = new int[1]; + if ((c.getChangeMask() & WindowContainerTransaction.Change.CHANGE_IGNORE_ORIENTATION_REQUEST) != 0) { - if (taskDisplayArea.setIgnoreOrientationRequest(c.getIgnoreOrientationRequest())) { - effects |= TRANSACT_EFFECTS_LIFECYCLE; + if (displayArea.setIgnoreOrientationRequest(c.getIgnoreOrientationRequest())) { + effects[0] |= TRANSACT_EFFECTS_LIFECYCLE; } } - return effects; - } - - private int applyDisplayAreaChanges(WindowContainer container, - WindowContainerTransaction.Change c) { - final int[] effects = new int[1]; - - container.forAllTasks(task -> { + displayArea.forAllTasks(task -> { Task tr = (Task) task; if ((c.getChangeMask() & WindowContainerTransaction.Change.CHANGE_HIDDEN) != 0) { if (tr.setForceHidden(FLAG_FORCE_HIDDEN_FOR_TASK_ORG, c.getHidden())) { @@ -475,10 +469,8 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub int effects = applyChanges(wc, c); - if (wc instanceof TaskDisplayArea) { - effects |= applyTaskDisplayAreaChanges((TaskDisplayArea) wc, c); - } else if (wc instanceof DisplayArea) { - effects |= applyDisplayAreaChanges(wc, c); + if (wc instanceof DisplayArea) { + effects |= applyDisplayAreaChanges(wc.asDisplayArea(), c); } else if (wc instanceof Task) { effects |= applyTaskChanges(wc.asTask(), c); } diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java index e8a7a9c1df92..2e7905c64049 100644 --- a/services/core/java/com/android/server/wm/WindowProcessController.java +++ b/services/core/java/com/android/server/wm/WindowProcessController.java @@ -1257,20 +1257,27 @@ public class WindowProcessController extends ConfigurationContainer<Configuratio mAtm.mStackSupervisor.removeHistoryRecords(this); boolean hasVisibleActivities = false; - if (mInactiveActivities != null && !mInactiveActivities.isEmpty()) { + final boolean hasInactiveActivities = + mInactiveActivities != null && !mInactiveActivities.isEmpty(); + final ArrayList<ActivityRecord> activities = + (mHasActivities || hasInactiveActivities) ? new ArrayList<>() : mActivities; + if (mHasActivities) { + activities.addAll(mActivities); + } + if (hasInactiveActivities) { // Make sure that all activities in this process are handled. - mActivities.addAll(mInactiveActivities); + activities.addAll(mInactiveActivities); } if (isRemoved()) { // The package of the died process should be force-stopped, so make its activities as // finishing to prevent the process from being started again if the next top (or being // visible) activity also resides in the same process. This must be done before removal. - for (int i = mActivities.size() - 1; i >= 0; i--) { - mActivities.get(i).makeFinishingLocked(); + for (int i = activities.size() - 1; i >= 0; i--) { + activities.get(i).makeFinishingLocked(); } } - for (int i = mActivities.size() - 1; i >= 0; i--) { - final ActivityRecord r = mActivities.get(i); + for (int i = activities.size() - 1; i >= 0; i--) { + final ActivityRecord r = activities.get(i); if (r.mVisibleRequested || r.isVisible()) { // While an activity launches a new activity, it's possible that the old activity // is already requested to be hidden (mVisibleRequested=false), but this visibility diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 0b53bf6ca8f6..d4b6d00c1679 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -224,6 +224,7 @@ import android.view.InputEventReceiver; import android.view.InputWindowHandle; import android.view.InsetsSource; import android.view.InsetsState; +import android.view.InsetsState.InternalInsetsType; import android.view.Surface.Rotation; import android.view.SurfaceControl; import android.view.SurfaceSession; @@ -719,20 +720,20 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP private final WindowProcessController mWpcForDisplayConfigChanges; /** - * @return The insets state as requested by the client, i.e. the dispatched insets state - * for which the visibilities are overridden with what the client requested. + * Returns the visibility of the given {@link InternalInsetsType type} requested by the client. + * + * @param type the given {@link InternalInsetsType type}. + * @return {@code true} if the type is requested visible. */ @Override - public InsetsState getRequestedInsetsState() { - return mRequestedInsetsState; + public boolean getRequestedVisibility(@InternalInsetsType int type) { + return mRequestedInsetsState.getSourceOrDefaultVisibility(type); } /** - * @see #getRequestedInsetsState() + * @see #getRequestedVisibility(int) */ - void updateRequestedInsetsState(InsetsState state) { - - // Only update the sources the client is actually controlling. + void updateRequestedVisibility(InsetsState state) { for (int i = 0; i < InsetsState.SIZE; i++) { final InsetsSource source = state.peekSource(i); if (source == null) continue; @@ -2172,16 +2173,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP } } - @Override - void forceWindowsScaleableInTransaction(boolean force) { - if (mWinAnimator != null && mWinAnimator.hasSurface()) { - mWinAnimator.mSurfaceController.forceScaleableInTransaction(force); - } - - super.forceWindowsScaleableInTransaction(force); - } - - @Override + @Override void removeImmediately() { super.removeImmediately(); @@ -3065,7 +3057,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP } void setForceHideNonSystemOverlayWindowIfNeeded(boolean forceHide) { - if (mOwnerCanAddInternalSystemWindow + if (!mSession.mOverlaysCanBeHidden || (!isSystemAlertWindowType(mAttrs.type) && mAttrs.type != TYPE_TOAST)) { return; } @@ -5779,7 +5771,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP } SurfaceControl getClientViewRootSurface() { - return mWinAnimator.getClientViewRootSurface(); + return mWinAnimator.getSurfaceControl(); } @Override diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java index f3429769893f..0111d4883eea 100644 --- a/services/core/java/com/android/server/wm/WindowStateAnimator.java +++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java @@ -359,8 +359,8 @@ class WindowStateAnimator { // surface before destroying it. if (mSurfaceController != null && mPendingDestroySurface != null) { mPostDrawTransaction.reparentChildren( - mSurfaceController.getClientViewRootSurface(), - mPendingDestroySurface.getClientViewRootSurface()).apply(); + mSurfaceController.mSurfaceControl, + mPendingDestroySurface.mSurfaceControl).apply(); } destroySurfaceLocked(); mSurfaceDestroyDeferred = true; @@ -371,7 +371,7 @@ class WindowStateAnimator { // Our SurfaceControl is always at layer 0 within the parent Surface managed by // window-state. We want this old Surface to stay on top of the new one // until we do the swap, so we place it at a positive layer. - t.setLayer(mSurfaceController.getClientViewRootSurface(), PRESERVED_SURFACE_LAYER); + t.setLayer(mSurfaceController.mSurfaceControl, PRESERVED_SURFACE_LAYER); } mDestroyPreservedSurfaceUponRedraw = true; mSurfaceDestroyDeferred = true; @@ -393,8 +393,8 @@ class WindowStateAnimator { && !mPendingDestroySurface.mChildrenDetached && (mWin.mActivityRecord == null || !mWin.mActivityRecord.isRelaunching())) { mPostDrawTransaction.reparentChildren( - mPendingDestroySurface.getClientViewRootSurface(), - mSurfaceController.getClientViewRootSurface()).apply(); + mPendingDestroySurface.mSurfaceControl, + mSurfaceController.mSurfaceControl).apply(); } destroyDeferredSurfaceLocked(); @@ -859,6 +859,7 @@ class WindowStateAnimator { if (displayed) { w.mToken.hasVisible = true; + mSurfaceController.setBackgroundBlurRadius(w.mAttrs.backgroundBlurRadius); } } @@ -971,10 +972,6 @@ class WindowStateAnimator { * @return Returns true if the surface was successfully shown. */ private boolean showSurfaceRobustlyLocked() { - if (mWin.getWindowConfiguration().windowsAreScaleable()) { - mSurfaceController.forceScaleableInTransaction(true); - } - boolean shown = mSurfaceController.showRobustlyInTransaction(); if (!shown) return false; @@ -988,8 +985,8 @@ class WindowStateAnimator { // Instead let the children get removed when the old surface is deleted. if (!mPendingDestroySurface.mChildrenDetached) { mPostDrawTransaction.reparentChildren( - mPendingDestroySurface.getClientViewRootSurface(), - mSurfaceController.getClientViewRootSurface()); + mPendingDestroySurface.mSurfaceControl, + mSurfaceController.mSurfaceControl); } } @@ -1205,10 +1202,10 @@ class WindowStateAnimator { mOffsetPositionForStackResize = offsetPositionForStackResize; } - SurfaceControl getClientViewRootSurface() { + SurfaceControl getSurfaceControl() { if (!hasSurface()) { return null; } - return mSurfaceController.getClientViewRootSurface(); + return mSurfaceController.mSurfaceControl; } } diff --git a/services/core/java/com/android/server/wm/WindowSurfaceController.java b/services/core/java/com/android/server/wm/WindowSurfaceController.java index cbe0a4232c97..21cb9a74c74e 100644 --- a/services/core/java/com/android/server/wm/WindowSurfaceController.java +++ b/services/core/java/com/android/server/wm/WindowSurfaceController.java @@ -50,11 +50,6 @@ class WindowSurfaceController { SurfaceControl mSurfaceControl; - /** - * WM only uses for deferred transactions. - */ - SurfaceControl mBLASTSurfaceControl; - // Should only be set from within setShown(). private boolean mSurfaceShown = false; private float mSurfaceX = 0; @@ -68,6 +63,8 @@ class WindowSurfaceController { private float mLastDsdy = 0; private float mLastDtdy = 1; + private int mLastBackgroundBlurRadius = 0; + private float mSurfaceAlpha = 0; private int mSurfaceLayer = 0; @@ -112,22 +109,13 @@ class WindowSurfaceController { final boolean useBLAST = mService.mUseBLAST && ((win.getAttrs().privateFlags & WindowManager.LayoutParams.PRIVATE_FLAG_USE_BLAST) != 0); + if (useBLAST) { - b.setContainerLayer(); + b.setBLASTLayer(); } mSurfaceControl = b.build(); - if (useBLAST) { - mBLASTSurfaceControl = win.makeSurface() - .setParent(mSurfaceControl) - .setName(name + "(BLAST)") - .setHidden(false) - .setBLASTLayer() - .setCallsite("WindowSurfaceController") - .build(); - } - Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); } @@ -172,9 +160,6 @@ class WindowSurfaceController { } finally { setShown(false); mSurfaceControl = null; - if (mBLASTSurfaceControl != null) { - mBLASTSurfaceControl.release(); - } } } @@ -285,6 +270,26 @@ class WindowSurfaceController { } } + void setBackgroundBlurRadius(int radius) { + ProtoLog.i(WM_SHOW_TRANSACTIONS, "SURFACE backgroundBlur=%o: %s", radius, title); + + if (mSurfaceControl == null || radius == mLastBackgroundBlurRadius) { + return; + } + mLastBackgroundBlurRadius = radius; + + if (SHOW_LIGHT_TRANSACTIONS) Slog.i(TAG, ">>> OPEN TRANSACTION setBackgroundBlurRadius"); + mService.openSurfaceTransaction(); + try { + mSurfaceControl.setBackgroundBlurRadius(radius); + } finally { + mService.closeSurfaceTransaction("setBackgroundBlurRadius"); + if (SHOW_LIGHT_TRANSACTIONS) { + Slog.i(TAG, "<<< CLOSE TRANSACTION setBackgroundBlurRadius"); + } + } + } + void setSecure(boolean isSecure) { ProtoLog.i(WM_SHOW_TRANSACTIONS, "SURFACE isSecure=%b: %s", isSecure, title); @@ -342,11 +347,9 @@ class WindowSurfaceController { return false; } - void forceScaleableInTransaction(boolean force) { - // -1 means we don't override the default or client specified - // scaling mode. - int scalingMode = force ? SCALING_MODE_SCALE_TO_WINDOW : -1; - mSurfaceControl.setOverrideScalingMode(scalingMode); + void deferTransactionUntil(SurfaceControl barrier, long frame) { + // TODO: Logging + mSurfaceControl.deferTransactionUntil(barrier, frame); } boolean clearWindowContentFrameStats() { @@ -371,12 +374,6 @@ class WindowSurfaceController { outSurfaceControl.copyFrom(mSurfaceControl, "WindowSurfaceController.getSurfaceControl"); } - void getBLASTSurfaceControl(SurfaceControl outSurfaceControl) { - if (mBLASTSurfaceControl != null) { - outSurfaceControl.copyFrom(mBLASTSurfaceControl, "WindowSurfaceController.getBLASTSurfaceControl"); - } - } - boolean getShown() { return mSurfaceShown; } @@ -401,21 +398,6 @@ class WindowSurfaceController { return mSurfaceH; } - /** - * Returns the Surface which the client-framework ViewRootImpl will be using. - * This is either the WSA SurfaceControl or it's BLAST child surface. - * This has too main uses: - * 1. This is the Surface the client will add children to, we use this to make - * sure we don't reparent the BLAST surface itself when calling reparentChildren - * 2. We use this as the barrier Surface for some deferTransaction operations. - */ - SurfaceControl getClientViewRootSurface() { - if (mBLASTSurfaceControl != null) { - return mBLASTSurfaceControl; - } - return mSurfaceControl; - } - void dumpDebug(ProtoOutputStream proto, long fieldId) { final long token = proto.start(fieldId); proto.write(SHOWN, mSurfaceShown); diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp index 30f6fa68396e..d6a56ba531d0 100644 --- a/services/core/jni/Android.bp +++ b/services/core/jni/Android.bp @@ -21,6 +21,7 @@ cc_library_static { "BroadcastRadio/TunerCallback.cpp", "BroadcastRadio/convert.cpp", "BroadcastRadio/regions.cpp", + "gnss/GnssConfiguration.cpp", "stats/PowerStatsPuller.cpp", "stats/SubsystemSleepStatePuller.cpp", "com_android_server_adb_AdbDebuggingManager.cpp", diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp index 91645ba1a9b9..e9d048a69966 100644 --- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp +++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp @@ -14,8 +14,8 @@ * limitations under the License. */ -#define LOG_TAG "GnssLocationProvider" - +// Define LOG_TAG and LOG_NDEBUG before <log/log.h> to overwrite the default values. +#define LOG_TAG "GnssLocationProviderJni" #define LOG_NDEBUG 0 #include <android/hardware/gnss/1.0/IGnss.h> @@ -39,6 +39,7 @@ #include <nativehelper/JNIHelp.h> #include "android_runtime/AndroidRuntime.h" #include "android_runtime/Log.h" +#include "gnss/GnssConfiguration.h" #include "hardware_legacy/power.h" #include "jni.h" #include "utils/Log.h" @@ -59,7 +60,6 @@ static jclass class_gnssMeasurement; static jclass class_location; static jclass class_gnssNavigationMessage; static jclass class_gnssClock; -static jclass class_gnssConfiguration_halInterfaceVersion; static jclass class_gnssAntennaInfoBuilder; static jclass class_phaseCenterOffset; static jclass class_sphericalCorrections; @@ -125,7 +125,6 @@ static jmethodID method_locationCtor; static jmethodID method_gnssNavigationMessageCtor; static jmethodID method_gnssClockCtor; static jmethodID method_gnssMeasurementCtor; -static jmethodID method_halInterfaceVersionCtor; static jmethodID method_gnssAntennaInfoBuilderCtor; static jmethodID method_phaseCenterOffsetCtor; static jmethodID method_sphericalCorrectionsCtor; @@ -149,6 +148,7 @@ using android::status_t; using android::String16; using android::wp; using android::binder::Status; +using android::gnss::GnssConfigurationInterface; using android::hardware::Return; using android::hardware::Void; @@ -156,7 +156,6 @@ using android::hardware::hidl_vec; using android::hardware::hidl_string; using android::hardware::hidl_death_recipient; -using android::hardware::gnss::PsdsType; using android::hardware::gnss::V1_0::GnssLocationFlags; using android::hardware::gnss::V1_0::IAGnssRilCallback; using android::hardware::gnss::V1_0::IGnssGeofenceCallback; @@ -193,10 +192,6 @@ using IGnss_V3_0 = android::hardware::gnss::V3_0::IGnss; using IGnssCallback_V1_0 = android::hardware::gnss::V1_0::IGnssCallback; using IGnssCallback_V2_0 = android::hardware::gnss::V2_0::IGnssCallback; using IGnssCallback_V2_1 = android::hardware::gnss::V2_1::IGnssCallback; -using IGnssConfiguration_V1_0 = android::hardware::gnss::V1_0::IGnssConfiguration; -using IGnssConfiguration_V1_1 = android::hardware::gnss::V1_1::IGnssConfiguration; -using IGnssConfiguration_V2_0 = android::hardware::gnss::V2_0::IGnssConfiguration; -using IGnssConfiguration_V2_1 = android::hardware::gnss::V2_1::IGnssConfiguration; using IGnssDebug_V1_0 = android::hardware::gnss::V1_0::IGnssDebug; using IGnssDebug_V2_0 = android::hardware::gnss::V2_0::IGnssDebug; using IGnssAntennaInfo = android::hardware::gnss::V2_1::IGnssAntennaInfo; @@ -228,9 +223,13 @@ using android::hardware::gnss::measurement_corrections::V1_0::GnssSingleSatCorre using android::hardware::gnss::visibility_control::V1_0::IGnssVisibilityControl; using android::hardware::gnss::visibility_control::V1_0::IGnssVisibilityControlCallback; +using android::hardware::gnss::BlocklistedSource; +using android::hardware::gnss::GnssConstellationType; +using android::hardware::gnss::PsdsType; using IGnssAidl = android::hardware::gnss::IGnss; using IGnssPsdsAidl = android::hardware::gnss::IGnssPsds; using IGnssPsdsCallbackAidl = android::hardware::gnss::IGnssPsdsCallback; +using IGnssConfigurationAidl = android::hardware::gnss::IGnssConfiguration; struct GnssDeathRecipient : virtual public hidl_death_recipient { @@ -266,10 +265,6 @@ sp<IGnssBatching_V1_0> gnssBatchingIface = nullptr; sp<IGnssBatching_V2_0> gnssBatchingIface_V2_0 = nullptr; sp<IGnssDebug_V1_0> gnssDebugIface = nullptr; sp<IGnssDebug_V2_0> gnssDebugIface_V2_0 = nullptr; -sp<IGnssConfiguration_V1_0> gnssConfigurationIface = nullptr; -sp<IGnssConfiguration_V1_1> gnssConfigurationIface_V1_1 = nullptr; -sp<IGnssConfiguration_V2_0> gnssConfigurationIface_V2_0 = nullptr; -sp<IGnssConfiguration_V2_1> gnssConfigurationIface_V2_1 = nullptr; sp<IGnssNi> gnssNiIface = nullptr; sp<IGnssMeasurement_V1_0> gnssMeasurementIface = nullptr; sp<IGnssMeasurement_V1_1> gnssMeasurementIface_V1_1 = nullptr; @@ -281,6 +276,8 @@ sp<IMeasurementCorrections_V1_1> gnssCorrectionsIface_V1_1 = nullptr; sp<IGnssVisibilityControl> gnssVisibilityControlIface = nullptr; sp<IGnssAntennaInfo> gnssAntennaInfoIface = nullptr; +std::unique_ptr<GnssConfigurationInterface> gnssConfigurationIface = nullptr; + #define WAKE_LOCK_NAME "GPS" namespace android { @@ -455,25 +452,14 @@ static void checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodNa } } -static jboolean checkAidlStatus(const Status& status, const char* errorMessage, - const bool success) { +static jboolean checkAidlStatus(const Status& status, const char* errorMessage) { if (!status.isOk()) { ALOGE("%s AIDL transport error: %s", errorMessage, status.toString8().c_str()); return JNI_FALSE; } - if (!success) { - ALOGE("AIDL return failure: %s", errorMessage); - return JNI_FALSE; - } return JNI_TRUE; } -static jobject createHalInterfaceVersionJavaObject(JNIEnv* env, jint major, jint minor) { - jobject version = env->NewObject(class_gnssConfiguration_halInterfaceVersion, - method_halInterfaceVersionCtor, major, minor); - return version; -} - struct ScopedJniString { ScopedJniString(JNIEnv* env, jstring javaString) : mEnv(env), mJavaString(javaString) { mNativeString = mEnv->GetStringUTFChars(mJavaString, nullptr); @@ -2160,13 +2146,6 @@ static void android_location_GnssNative_class_init_once(JNIEnv* env, jclass claz class_gnssClock = (jclass) env->NewGlobalRef(gnssClockClass); method_gnssClockCtor = env->GetMethodID(class_gnssClock, "<init>", "()V"); - jclass gnssConfiguration_halInterfaceVersionClass = env->FindClass( - "com/android/server/location/gnss/GnssConfiguration$HalInterfaceVersion"); - class_gnssConfiguration_halInterfaceVersion = - (jclass) env->NewGlobalRef(gnssConfiguration_halInterfaceVersionClass); - method_halInterfaceVersionCtor = - env->GetMethodID(class_gnssConfiguration_halInterfaceVersion, "<init>", "(II)V"); - jclass arrayListClass = env->FindClass("java/util/ArrayList"); class_arrayList = (jclass)env->NewGlobalRef(arrayListClass); method_arrayListCtor = env->GetMethodID(class_arrayList, "<init>", "()V"); @@ -2174,6 +2153,8 @@ static void android_location_GnssNative_class_init_once(JNIEnv* env, jclass claz jclass doubleArrayClass = env->FindClass("[D"); class_doubleArray = (jclass)env->NewGlobalRef(doubleArrayClass); + + gnss::GnssConfiguration_class_init_once(env); } /* Initialization needed at system boot and whenever GNSS service dies. */ @@ -2371,39 +2352,41 @@ static void android_location_GnssNative_init_once(JNIEnv* env, jobject obj, gnssNiIface = gnssNi; } - if (gnssHal_V2_1 != nullptr) { + if (gnssHalAidl != nullptr) { + sp<IGnssConfigurationAidl> gnssConfigurationAidl; + auto status = gnssHalAidl->getExtensionGnssConfiguration(&gnssConfigurationAidl); + if (checkAidlStatus(status, + "Unable to get a handle to GnssConfiguration AIDL interface.")) { + gnssConfigurationIface = + std::make_unique<android::gnss::GnssConfiguration>(gnssConfigurationAidl); + } + } else if (gnssHal_V2_1 != nullptr) { auto gnssConfiguration = gnssHal_V2_1->getExtensionGnssConfiguration_2_1(); - if (!gnssConfiguration.isOk()) { - ALOGD("Unable to get a handle to GnssConfiguration_V2_1"); - } else { - gnssConfigurationIface_V2_1 = gnssConfiguration; - gnssConfigurationIface_V2_0 = gnssConfigurationIface_V2_1; - gnssConfigurationIface_V1_1 = gnssConfigurationIface_V2_1; - gnssConfigurationIface = gnssConfigurationIface_V2_1; + if (checkHidlReturn(gnssConfiguration, + "Unable to get a handle to GnssConfiguration_V2_1")) { + gnssConfigurationIface = + std::make_unique<android::gnss::GnssConfiguration_V2_1>(gnssConfiguration); } } else if (gnssHal_V2_0 != nullptr) { auto gnssConfiguration = gnssHal_V2_0->getExtensionGnssConfiguration_2_0(); - if (!gnssConfiguration.isOk()) { - ALOGD("Unable to get a handle to GnssConfiguration_V2_0"); - } else { - gnssConfigurationIface_V2_0 = gnssConfiguration; - gnssConfigurationIface_V1_1 = gnssConfigurationIface_V2_0; - gnssConfigurationIface = gnssConfigurationIface_V2_0; + if (checkHidlReturn(gnssConfiguration, + "Unable to get a handle to GnssConfiguration_V2_0")) { + gnssConfigurationIface = + std::make_unique<android::gnss::GnssConfiguration_V2_0>(gnssConfiguration); } } else if (gnssHal_V1_1 != nullptr) { auto gnssConfiguration = gnssHal_V1_1->getExtensionGnssConfiguration_1_1(); - if (!gnssConfiguration.isOk()) { - ALOGD("Unable to get a handle to GnssConfiguration_V1_1"); - } else { - gnssConfigurationIface_V1_1 = gnssConfiguration; - gnssConfigurationIface = gnssConfigurationIface_V1_1; + if (checkHidlReturn(gnssConfiguration, + "Unable to get a handle to GnssConfiguration_V1_1")) { + gnssConfigurationIface = + std::make_unique<android::gnss::GnssConfiguration_V1_1>(gnssConfiguration); } } else { - auto gnssConfiguration_V1_0 = gnssHal->getExtensionGnssConfiguration(); - if (!gnssConfiguration_V1_0.isOk()) { - ALOGD("Unable to get a handle to GnssConfiguration"); - } else { - gnssConfigurationIface = gnssConfiguration_V1_0; + auto gnssConfiguration = gnssHal->getExtensionGnssConfiguration(); + if (checkHidlReturn(gnssConfiguration, + "Unable to get a handle to GnssConfiguration_V1_0")) { + gnssConfigurationIface = + std::make_unique<android::gnss::GnssConfiguration_V1_0>(gnssConfiguration); } } @@ -2459,25 +2442,10 @@ static jboolean android_location_GnssNetworkConnectivityHandler_is_agps_ril_supp static jobject android_location_GnssConfiguration_get_gnss_configuration_version( JNIEnv* env, jclass /* jclazz */) { - jint major, minor; - if (gnssConfigurationIface_V2_1 != nullptr) { - major = 2; - minor = 1; - } - else if (gnssConfigurationIface_V2_0 != nullptr) { - major = 2; - minor = 0; - } else if (gnssConfigurationIface_V1_1 != nullptr) { - major = 1; - minor = 1; - } else if (gnssConfigurationIface != nullptr) { - major = 1; - minor = 0; - } else { + if (gnssConfigurationIface == nullptr) { return nullptr; } - - return createHalInterfaceVersionJavaObject(env, major, minor); + return gnssConfigurationIface->getVersion(env); } /* Initialization needed each time the GPS service is shutdown. */ @@ -2519,9 +2487,8 @@ static jboolean android_location_GnssLocationProvider_init(JNIEnv* /* env */, jo // Set IGnssPsds or IGnssXtra callback. if (gnssPsdsAidlIface != nullptr) { sp<IGnssPsdsCallbackAidl> gnssPsdsCallbackAidl = new GnssPsdsCallbackAidl(); - bool success; - auto status = gnssPsdsAidlIface->setCallback(gnssPsdsCallbackAidl, &success); - if (!checkAidlStatus(status, "IGnssPsdsAidl setCallback() failed.", success)) { + auto status = gnssPsdsAidlIface->setCallback(gnssPsdsCallbackAidl); + if (!checkAidlStatus(status, "IGnssPsdsAidl setCallback() failed.")) { gnssPsdsAidlIface = nullptr; } } else { @@ -2814,13 +2781,11 @@ static void android_location_GnssLocationProvider_inject_psds_data(JNIEnv* env, jbyte* bytes = reinterpret_cast<jbyte *>(env->GetPrimitiveArrayCritical(data, 0)); if (gnssPsdsAidlIface != nullptr) { - bool success; auto status = gnssPsdsAidlIface->injectPsdsData(static_cast<PsdsType>(psdsType), std::vector<uint8_t>((const uint8_t*)bytes, (const uint8_t*)bytes + - length), - &success); - checkAidlStatus(status, "IGnssPsdsAidl injectPsdsData() failed.", success); + length)); + checkAidlStatus(status, "IGnssPsdsAidl injectPsdsData() failed."); } else if (gnssPsdsIface != nullptr) { auto result = gnssPsdsIface->injectPsdsData_3_0(psdsType, std::string((const char*)bytes, length)); @@ -3486,9 +3451,7 @@ static jboolean android_location_GnssConfiguration_set_emergency_supl_pdn(JNIEnv ALOGE("%s: IGnssConfiguration interface not available.", __func__); return JNI_FALSE; } - - auto result = gnssConfigurationIface->setEmergencySuplPdn(emergencySuplPdn); - return checkHidlReturn(result, "IGnssConfiguration setEmergencySuplPdn() failed."); + return gnssConfigurationIface->setEmergencySuplPdn(emergencySuplPdn); } static jboolean android_location_GnssConfiguration_set_supl_version(JNIEnv*, @@ -3498,25 +3461,17 @@ static jboolean android_location_GnssConfiguration_set_supl_version(JNIEnv*, ALOGE("%s: IGnssConfiguration interface not available.", __func__); return JNI_FALSE; } - auto result = gnssConfigurationIface->setSuplVersion(version); - return checkHidlReturn(result, "IGnssConfiguration setSuplVersion() failed."); + return gnssConfigurationIface->setSuplVersion(version); } static jboolean android_location_GnssConfiguration_set_supl_es(JNIEnv*, jobject, jint suplEs) { - if (gnssConfigurationIface_V2_0 != nullptr || gnssConfigurationIface_V2_1 != nullptr) { - ALOGI("Config parameter SUPL_ES is deprecated in IGnssConfiguration.hal version 2.0 and higher."); - return JNI_FALSE; - } - if (gnssConfigurationIface == nullptr) { ALOGE("%s: IGnssConfiguration interface not available.", __func__); return JNI_FALSE; } - - auto result = gnssConfigurationIface->setSuplEs(suplEs); - return checkHidlReturn(result, "IGnssConfiguration setSuplEs() failed."); + return gnssConfigurationIface->setSuplEs(suplEs); } static jboolean android_location_GnssConfiguration_set_supl_mode(JNIEnv*, @@ -3526,26 +3481,17 @@ static jboolean android_location_GnssConfiguration_set_supl_mode(JNIEnv*, ALOGE("%s: IGnssConfiguration interface not available.", __func__); return JNI_FALSE; } - - auto result = gnssConfigurationIface->setSuplMode(mode); - return checkHidlReturn(result, "IGnssConfiguration setSuplMode() failed."); + return gnssConfigurationIface->setSuplMode(mode); } static jboolean android_location_GnssConfiguration_set_gps_lock(JNIEnv*, jobject, jint gpsLock) { - if (gnssConfigurationIface_V2_0 != nullptr || gnssConfigurationIface_V2_1 != nullptr) { - ALOGI("Config parameter GPS_LOCK is deprecated in IGnssConfiguration.hal version 2.0."); - return JNI_FALSE; - } - if (gnssConfigurationIface == nullptr) { ALOGE("%s: IGnssConfiguration interface not available.", __func__); return JNI_FALSE; } - - auto result = gnssConfigurationIface->setGpsLock(gpsLock); - return checkHidlReturn(result, "IGnssConfiguration setGpsLock() failed."); + return gnssConfigurationIface->setGpsLock(gpsLock); } static jboolean android_location_GnssConfiguration_set_lpp_profile(JNIEnv*, @@ -3555,9 +3501,7 @@ static jboolean android_location_GnssConfiguration_set_lpp_profile(JNIEnv*, ALOGE("%s: IGnssConfiguration interface not available.", __func__); return JNI_FALSE; } - - auto result = gnssConfigurationIface->setLppProfile(lppProfile); - return checkHidlReturn(result, "IGnssConfiguration setLppProfile() failed."); + return gnssConfigurationIface->setLppProfile(lppProfile); } static jboolean android_location_GnssConfiguration_set_gnss_pos_protocol_select(JNIEnv*, @@ -3567,59 +3511,16 @@ static jboolean android_location_GnssConfiguration_set_gnss_pos_protocol_select( ALOGE("%s: IGnssConfiguration interface not available.", __func__); return JNI_FALSE; } - - auto result = gnssConfigurationIface->setGlonassPositioningProtocol(gnssPosProtocol); - return checkHidlReturn(result, "IGnssConfiguration setGlonassPositioningProtocol() failed."); + return gnssConfigurationIface->setGlonassPositioningProtocol(gnssPosProtocol); } static jboolean android_location_GnssConfiguration_set_satellite_blacklist( JNIEnv* env, jobject, jintArray constellations, jintArray sv_ids) { - if (gnssConfigurationIface_V1_1 == nullptr && gnssConfigurationIface_V2_1 == nullptr) { + if (gnssConfigurationIface == nullptr) { ALOGI("IGnssConfiguration interface does not support satellite blacklist."); return JNI_FALSE; } - - jint *constellation_array = env->GetIntArrayElements(constellations, 0); - if (nullptr == constellation_array) { - ALOGI("GetIntArrayElements returns nullptr."); - return JNI_FALSE; - } - jsize length = env->GetArrayLength(constellations); - - jint *sv_id_array = env->GetIntArrayElements(sv_ids, 0); - if (nullptr == sv_id_array) { - ALOGI("GetIntArrayElements returns nullptr."); - return JNI_FALSE; - } - - if (length != env->GetArrayLength(sv_ids)) { - ALOGI("Lengths of constellations and sv_ids are inconsistent."); - return JNI_FALSE; - } - - if (gnssConfigurationIface_V2_1 != nullptr) { - hidl_vec<IGnssConfiguration_V2_1::BlacklistedSource> sources; - sources.resize(length); - - for (int i = 0; i < length; i++) { - sources[i].constellation = static_cast<GnssConstellationType_V2_0>(constellation_array[i]); - sources[i].svid = sv_id_array[i]; - } - - auto result = gnssConfigurationIface_V2_1->setBlacklist_2_1(sources); - return checkHidlReturn(result, "IGnssConfiguration_V2_1 setBlacklist_2_1() failed."); - } - - hidl_vec<IGnssConfiguration_V1_1::BlacklistedSource> sources; - sources.resize(length); - - for (int i = 0; i < length; i++) { - sources[i].constellation = static_cast<GnssConstellationType_V1_0>(constellation_array[i]); - sources[i].svid = sv_id_array[i]; - } - - auto result = gnssConfigurationIface_V1_1->setBlacklist(sources); - return checkHidlReturn(result, "IGnssConfiguration setBlacklist() failed."); + return gnssConfigurationIface->setBlocklist(env, constellations, sv_ids); } static jboolean android_location_GnssConfiguration_set_es_extension_sec( @@ -3628,15 +3529,7 @@ static jboolean android_location_GnssConfiguration_set_es_extension_sec( ALOGE("%s: IGnssConfiguration interface not available.", __func__); return JNI_FALSE; } - - if (gnssConfigurationIface_V2_0 == nullptr) { - ALOGI("Config parameter ES_EXTENSION_SEC is not supported in IGnssConfiguration.hal" - " versions earlier than 2.0."); - return JNI_FALSE; - } - - auto result = gnssConfigurationIface_V2_0->setEsExtensionSec(emergencyExtensionSeconds); - return checkHidlReturn(result, "IGnssConfiguration setEsExtensionSec() failed."); + return gnssConfigurationIface->setEsExtensionSec(emergencyExtensionSeconds); } static jint android_location_GnssBatchingProvider_get_batch_size(JNIEnv*, jclass) { diff --git a/services/core/jni/gnss/GnssConfiguration.cpp b/services/core/jni/gnss/GnssConfiguration.cpp new file mode 100644 index 000000000000..8610c75e7e76 --- /dev/null +++ b/services/core/jni/gnss/GnssConfiguration.cpp @@ -0,0 +1,264 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// Define LOG_TAG before <log/log.h> to overwrite the default value. +#define LOG_TAG "GnssConfigurationJni" + +#include "GnssConfiguration.h" + +using android::hardware::gnss::GnssConstellationType; +using GnssConstellationType_V1_0 = android::hardware::gnss::V1_0::GnssConstellationType; +using GnssConstellationType_V2_0 = android::hardware::gnss::V2_0::GnssConstellationType; + +using android::binder::Status; +using android::hardware::hidl_vec; +using android::hardware::Return; + +using android::hardware::gnss::IGnssConfiguration; +using IGnssConfiguration_V1_0 = android::hardware::gnss::V1_0::IGnssConfiguration; +using IGnssConfiguration_V1_1 = android::hardware::gnss::V1_1::IGnssConfiguration; +using IGnssConfiguration_V2_0 = android::hardware::gnss::V2_0::IGnssConfiguration; +using IGnssConfiguration_V2_1 = android::hardware::gnss::V2_1::IGnssConfiguration; + +using android::hardware::gnss::BlocklistedSource; +using BlocklistedSource_V1_1 = IGnssConfiguration_V1_1::BlacklistedSource; +using BlocklistedSource_V2_1 = IGnssConfiguration_V2_1::BlacklistedSource; + +namespace { + +jclass class_gnssConfiguration_halInterfaceVersion; +jmethodID method_halInterfaceVersionCtor; + +jboolean checkAidlStatus(const Status& status, const char* errorMessage) { + if (!status.isOk()) { + ALOGE("%s AIDL transport error: %s", errorMessage, status.toString8().c_str()); + return JNI_FALSE; + } + return JNI_TRUE; +} + +template <class T> +inline void logHidlError(Return<T>& result, const char* errorMessage) { + ALOGE("%s HIDL transport error: %s", errorMessage, result.description().c_str()); +} + +template <class T> +jboolean checkHidlReturn(Return<T>& result, const char* errorMessage) { + if (!result.isOk()) { + logHidlError(result, errorMessage); + return JNI_FALSE; + } else { + return JNI_TRUE; + } +} + +jobject createHalInterfaceVersionJavaObject(JNIEnv* env, jint major, jint minor) { + return env->NewObject(class_gnssConfiguration_halInterfaceVersion, + method_halInterfaceVersionCtor, major, minor); +} + +} // anonymous namespace + +namespace android::gnss { + +void GnssConfiguration_class_init_once(JNIEnv* env) { + jclass gnssConfiguration_halInterfaceVersionClass = env->FindClass( + "com/android/server/location/gnss/GnssConfiguration$HalInterfaceVersion"); + class_gnssConfiguration_halInterfaceVersion = + (jclass)env->NewGlobalRef(gnssConfiguration_halInterfaceVersionClass); + method_halInterfaceVersionCtor = + env->GetMethodID(class_gnssConfiguration_halInterfaceVersion, "<init>", "(II)V"); +} + +// Implementation of GnssConfiguration (AIDL HAL) + +GnssConfiguration::GnssConfiguration(const sp<IGnssConfiguration>& iGnssConfiguration) + : mIGnssConfiguration(iGnssConfiguration) {} + +jobject GnssConfiguration::getVersion(JNIEnv* env) { + return createHalInterfaceVersionJavaObject(env, 3, 0); +} + +jboolean GnssConfiguration::setEmergencySuplPdn(jint enable) { + auto status = mIGnssConfiguration->setEmergencySuplPdn(enable); + return checkAidlStatus(status, "IGnssConfiguration setEmergencySuplPdn() failed."); +} + +jboolean GnssConfiguration::setSuplVersion(jint version) { + auto status = mIGnssConfiguration->setSuplVersion(version); + return checkAidlStatus(status, "IGnssConfiguration setSuplVersion() failed."); +} + +jboolean GnssConfiguration::setSuplEs(jint enable) { + ALOGI("Config parameter SUPL_ES is deprecated in IGnssConfiguration AIDL HAL."); + return JNI_FALSE; +} + +jboolean GnssConfiguration::setSuplMode(jint mode) { + auto status = mIGnssConfiguration->setSuplMode(mode); + return checkAidlStatus(status, "IGnssConfiguration setSuplMode() failed."); +} + +jboolean GnssConfiguration::setGpsLock(jint gpsLock) { + ALOGI("Config parameter GPS_LOCK is not supported in IGnssConfiguration AIDL HAL."); + return JNI_FALSE; +} + +jboolean GnssConfiguration::setLppProfile(jint lppProfile) { + auto status = mIGnssConfiguration->setLppProfile(lppProfile); + return checkAidlStatus(status, "IGnssConfiguration setLppProfile() failed."); +} + +jboolean GnssConfiguration::setGlonassPositioningProtocol(jint gnssPosProtocol) { + auto status = mIGnssConfiguration->setGlonassPositioningProtocol(gnssPosProtocol); + return checkAidlStatus(status, "IGnssConfiguration setGlonassPositioningProtocol() failed."); +} + +jboolean GnssConfiguration::setEsExtensionSec(jint emergencyExtensionSeconds) { + auto status = mIGnssConfiguration->setEsExtensionSec(emergencyExtensionSeconds); + return checkAidlStatus(status, "IGnssConfiguration setEsExtensionSec() failed."); +} + +jboolean GnssConfiguration::setBlocklist(JNIEnv* env, jintArray& constellations, + jintArray& sv_ids) { + auto sources = + getBlocklistedSources<BlocklistedSource, GnssConstellationType>(env, constellations, + sv_ids); + auto status = mIGnssConfiguration->setBlocklist(sources); + return checkAidlStatus(status, "IGnssConfiguration setBlocklist() failed."); +} + +// Implementation of GnssConfiguration_V1_0 + +GnssConfiguration_V1_0::GnssConfiguration_V1_0( + const sp<IGnssConfiguration_V1_0>& iGnssConfiguration) + : mIGnssConfiguration_V1_0(iGnssConfiguration) {} + +jobject GnssConfiguration_V1_0::getVersion(JNIEnv* env) { + return createHalInterfaceVersionJavaObject(env, 1, 0); +} + +jboolean GnssConfiguration_V1_0::setEmergencySuplPdn(jint enable) { + auto result = mIGnssConfiguration_V1_0->setEmergencySuplPdn(enable); + return checkHidlReturn(result, "IGnssConfiguration setEmergencySuplPdn() failed."); +} + +jboolean GnssConfiguration_V1_0::setSuplVersion(jint version) { + auto result = mIGnssConfiguration_V1_0->setSuplVersion(version); + return checkHidlReturn(result, "IGnssConfiguration setSuplVersion() failed."); +} + +jboolean GnssConfiguration_V1_0::setSuplEs(jint enable) { + auto result = mIGnssConfiguration_V1_0->setSuplEs(enable); + return checkHidlReturn(result, "IGnssConfiguration setSuplEs() failed."); +} + +jboolean GnssConfiguration_V1_0::setSuplMode(jint mode) { + auto result = mIGnssConfiguration_V1_0->setSuplMode(mode); + return checkHidlReturn(result, "IGnssConfiguration setSuplMode() failed."); +} + +jboolean GnssConfiguration_V1_0::setGpsLock(jint gpsLock) { + auto result = mIGnssConfiguration_V1_0->setGpsLock(gpsLock); + return checkHidlReturn(result, "IGnssConfiguration setGpsLock() failed."); +} + +jboolean GnssConfiguration_V1_0::setLppProfile(jint lppProfile) { + auto result = mIGnssConfiguration_V1_0->setLppProfile(lppProfile); + return checkHidlReturn(result, "IGnssConfiguration setLppProfile() failed."); +} + +jboolean GnssConfiguration_V1_0::setGlonassPositioningProtocol(jint gnssPosProtocol) { + auto result = mIGnssConfiguration_V1_0->setGlonassPositioningProtocol(gnssPosProtocol); + return checkHidlReturn(result, "IGnssConfiguration setGlonassPositioningProtocol() failed."); +} + +jboolean GnssConfiguration_V1_0::setEsExtensionSec(jint emergencyExtensionSeconds) { + ALOGI("Config parameter ES_EXTENSION_SEC is not supported in IGnssConfiguration.hal" + " versions earlier than 2.0."); + return JNI_FALSE; +} + +jboolean GnssConfiguration_V1_0::setBlocklist(JNIEnv* env, jintArray& constellations, + jintArray& sv_ids) { + ALOGI("IGnssConfiguration interface does not support satellite blocklist."); + return JNI_FALSE; +} + +// Implementation of GnssConfiguration_V1_1 + +GnssConfiguration_V1_1::GnssConfiguration_V1_1( + const sp<IGnssConfiguration_V1_1>& iGnssConfiguration) + : GnssConfiguration_V1_0{iGnssConfiguration}, mIGnssConfiguration_V1_1(iGnssConfiguration) {} + +jobject GnssConfiguration_V1_1::getVersion(JNIEnv* env) { + return createHalInterfaceVersionJavaObject(env, 1, 1); +} + +jboolean GnssConfiguration_V1_1::setBlocklist(JNIEnv* env, jintArray& constellations, + jintArray& sv_ids) { + auto sources = getBlocklistedSources<BlocklistedSource_V1_1, + GnssConstellationType_V1_0>(env, constellations, sv_ids); + auto result = mIGnssConfiguration_V1_1->setBlacklist(sources); + return checkHidlReturn(result, "IGnssConfiguration setBlocklist() failed."); +} + +// Implementation of GnssConfiguration_V2_0 + +GnssConfiguration_V2_0::GnssConfiguration_V2_0( + const sp<IGnssConfiguration_V2_0>& iGnssConfiguration) + : GnssConfiguration_V1_1{iGnssConfiguration}, mIGnssConfiguration_V2_0(iGnssConfiguration) {} + +jobject GnssConfiguration_V2_0::getVersion(JNIEnv* env) { + return createHalInterfaceVersionJavaObject(env, 2, 0); +} + +jboolean GnssConfiguration_V2_0::setSuplEs(jint enable) { + ALOGI("Config parameter SUPL_ES is deprecated in IGnssConfiguration.hal version 2.0 and " + "higher."); + return JNI_FALSE; +} + +jboolean GnssConfiguration_V2_0::setGpsLock(jint enable) { + ALOGI("Config parameter GPS_LOCK is deprecated in IGnssConfiguration.hal version 2.0 and " + "higher."); + return JNI_FALSE; +} + +jboolean GnssConfiguration_V2_0::setEsExtensionSec(jint emergencyExtensionSeconds) { + auto result = mIGnssConfiguration_V2_0->setEsExtensionSec(emergencyExtensionSeconds); + return checkHidlReturn(result, "IGnssConfiguration setEsExtensionSec() failed."); +} + +// Implementation of GnssConfiguration_V2_1 + +GnssConfiguration_V2_1::GnssConfiguration_V2_1( + const sp<IGnssConfiguration_V2_1>& iGnssConfiguration) + : GnssConfiguration_V2_0{iGnssConfiguration}, mIGnssConfiguration_V2_1(iGnssConfiguration) {} + +jobject GnssConfiguration_V2_1::getVersion(JNIEnv* env) { + return createHalInterfaceVersionJavaObject(env, 2, 1); +} + +jboolean GnssConfiguration_V2_1::setBlocklist(JNIEnv* env, jintArray& constellations, + jintArray& sv_ids) { + auto sources = getBlocklistedSources<BlocklistedSource_V2_1, + GnssConstellationType_V2_0>(env, constellations, sv_ids); + auto result = mIGnssConfiguration_V2_1->setBlacklist_2_1(sources); + return checkHidlReturn(result, "IGnssConfiguration setBlocklist() failed."); +} + +} // namespace android::gnss diff --git a/services/core/jni/gnss/GnssConfiguration.h b/services/core/jni/gnss/GnssConfiguration.h new file mode 100644 index 000000000000..ea0f178b7132 --- /dev/null +++ b/services/core/jni/gnss/GnssConfiguration.h @@ -0,0 +1,168 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _ANDROID_SERVER_GNSS_GNSSCONFIGURATION_H +#define _ANDROID_SERVER_GNSS_GNSSCONFIGURATION_H + +#pragma once + +#ifndef LOG_TAG +#error LOG_TAG must be defined before including this file. +#endif + +#include <android/hardware/gnss/1.0/IGnssConfiguration.h> +#include <android/hardware/gnss/1.1/IGnssConfiguration.h> +#include <android/hardware/gnss/2.0/IGnssConfiguration.h> +#include <android/hardware/gnss/2.1/IGnssConfiguration.h> +#include <android/hardware/gnss/BnGnssConfiguration.h> +#include <log/log.h> + +#include "jni.h" + +namespace android::gnss { + +void GnssConfiguration_class_init_once(JNIEnv* env); + +class GnssConfigurationInterface { +public: + virtual ~GnssConfigurationInterface() {} + virtual jobject getVersion(JNIEnv* env) = 0; + virtual jboolean setEmergencySuplPdn(jint enable) = 0; + virtual jboolean setSuplVersion(jint version) = 0; + virtual jboolean setSuplEs(jint enable) = 0; + virtual jboolean setSuplMode(jint mode) = 0; + virtual jboolean setGpsLock(jint gpsLock) = 0; + virtual jboolean setLppProfile(jint lppProfile) = 0; + virtual jboolean setGlonassPositioningProtocol(jint gnssPosProtocol) = 0; + virtual jboolean setEsExtensionSec(jint emergencyExtensionSeconds) = 0; + virtual jboolean setBlocklist(JNIEnv* env, jintArray& constellations, jintArray& sv_ids) = 0; + +protected: + template <class T_BlocklistSource, class T_ConstellationType> + hardware::hidl_vec<T_BlocklistSource> getBlocklistedSources(JNIEnv* env, + jintArray& constellations, + jintArray& sv_ids) { + jint* constellation_array = env->GetIntArrayElements(constellations, 0); + if (nullptr == constellation_array) { + ALOGI("GetIntArrayElements returns nullptr."); + return JNI_FALSE; + } + + jsize length = env->GetArrayLength(constellations); + + jint* sv_id_array = env->GetIntArrayElements(sv_ids, 0); + if (nullptr == sv_id_array) { + ALOGI("GetIntArrayElements returns nullptr."); + return JNI_FALSE; + } + + if (length != env->GetArrayLength(sv_ids)) { + ALOGI("Lengths of constellations and sv_ids are inconsistent."); + return JNI_FALSE; + } + + hardware::hidl_vec<T_BlocklistSource> sources; + sources.resize(length); + + for (int i = 0; i < length; i++) { + sources[i].constellation = static_cast<T_ConstellationType>(constellation_array[i]); + sources[i].svid = sv_id_array[i]; + } + + env->ReleaseIntArrayElements(constellations, constellation_array, 0); + env->ReleaseIntArrayElements(sv_ids, sv_id_array, 0); + + return sources; + } +}; + +class GnssConfiguration : public GnssConfigurationInterface { +public: + GnssConfiguration(const sp<android::hardware::gnss::IGnssConfiguration>& iGnssConfiguration); + jobject getVersion(JNIEnv* env) override; + jboolean setEmergencySuplPdn(jint enable) override; + jboolean setSuplVersion(jint version) override; + jboolean setSuplEs(jint enable) override; + jboolean setSuplMode(jint mode) override; + jboolean setGpsLock(jint gpsLock) override; + jboolean setLppProfile(jint lppProfile) override; + jboolean setGlonassPositioningProtocol(jint gnssPosProtocol) override; + jboolean setEsExtensionSec(jint emergencyExtensionSeconds) override; + jboolean setBlocklist(JNIEnv* env, jintArray& constellations, jintArray& sv_ids) override; + +private: + const sp<android::hardware::gnss::IGnssConfiguration> mIGnssConfiguration; +}; + +class GnssConfiguration_V1_0 : public GnssConfigurationInterface { +public: + GnssConfiguration_V1_0( + const sp<android::hardware::gnss::V1_0::IGnssConfiguration>& iGnssConfiguration); + jobject getVersion(JNIEnv* env) override; + jboolean setEmergencySuplPdn(jint enable); + jboolean setSuplVersion(jint version) override; + jboolean setSuplEs(jint enable) override; + jboolean setSuplMode(jint mode) override; + jboolean setGpsLock(jint gpsLock) override; + jboolean setLppProfile(jint lppProfile); + jboolean setGlonassPositioningProtocol(jint gnssPosProtocol) override; + jboolean setEsExtensionSec(jint emergencyExtensionSeconds) override; + jboolean setBlocklist(JNIEnv* env, jintArray& constellations, jintArray& sv_ids) override; + +private: + const sp<android::hardware::gnss::V1_0::IGnssConfiguration> mIGnssConfiguration_V1_0; +}; + +class GnssConfiguration_V1_1 : public GnssConfiguration_V1_0 { +public: + GnssConfiguration_V1_1( + const sp<android::hardware::gnss::V1_1::IGnssConfiguration>& iGnssConfiguration); + + jobject getVersion(JNIEnv* env) override; + + jboolean setBlocklist(JNIEnv* env, jintArray& constellations, jintArray& sv_ids) override; + +private: + const sp<android::hardware::gnss::V1_1::IGnssConfiguration> mIGnssConfiguration_V1_1; +}; + +class GnssConfiguration_V2_0 : public GnssConfiguration_V1_1 { +public: + GnssConfiguration_V2_0( + const sp<android::hardware::gnss::V2_0::IGnssConfiguration>& iGnssConfiguration); + jobject getVersion(JNIEnv* env) override; + jboolean setSuplEs(jint enable) override; + jboolean setGpsLock(jint enable) override; + jboolean setEsExtensionSec(jint emergencyExtensionSeconds) override; + +private: + const sp<android::hardware::gnss::V2_0::IGnssConfiguration> mIGnssConfiguration_V2_0; +}; + +class GnssConfiguration_V2_1 : public GnssConfiguration_V2_0 { +public: + GnssConfiguration_V2_1( + const sp<android::hardware::gnss::V2_1::IGnssConfiguration>& iGnssConfiguration); + jobject getVersion(JNIEnv* env) override; + jboolean setBlocklist(JNIEnv* env, jintArray& constellations, jintArray& sv_ids) override; + +private: + const sp<android::hardware::gnss::V2_1::IGnssConfiguration> mIGnssConfiguration_V2_1; +}; + +} // namespace android::gnss + +#endif // _ANDROID_SERVER_GNSS_GNSSCONFIGURATION_H diff --git a/services/core/xsd/platform-compat-config.xsd b/services/core/xsd/platform-compat-config.xsd index 5a4c682f52eb..992470816068 100644 --- a/services/core/xsd/platform-compat-config.xsd +++ b/services/core/xsd/platform-compat-config.xsd @@ -29,6 +29,7 @@ <xs:attribute type="xs:boolean" name="disabled"/> <xs:attribute type="xs:boolean" name="loggingOnly"/> <xs:attribute type="xs:int" name="enableAfterTargetSdk"/> + <xs:attribute type="xs:int" name="enableSinceTargetSdk"/> <xs:attribute type="xs:string" name="description"/> </xs:extension> </xs:simpleContent> diff --git a/services/core/xsd/platform-compat-schema/current.txt b/services/core/xsd/platform-compat-schema/current.txt index 7def58d56a26..e3640edd0201 100644 --- a/services/core/xsd/platform-compat-schema/current.txt +++ b/services/core/xsd/platform-compat-schema/current.txt @@ -6,6 +6,7 @@ package com.android.server.compat.config { method public String getDescription(); method public boolean getDisabled(); method public int getEnableAfterTargetSdk(); + method public int getEnableSinceTargetSdk(); method public long getId(); method public boolean getLoggingOnly(); method public String getName(); @@ -13,6 +14,7 @@ package com.android.server.compat.config { method public void setDescription(String); method public void setDisabled(boolean); method public void setEnableAfterTargetSdk(int); + method public void setEnableSinceTargetSdk(int); method public void setId(long); method public void setLoggingOnly(boolean); method public void setName(String); diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/CertificateMonitor.java b/services/devicepolicy/java/com/android/server/devicepolicy/CertificateMonitor.java index aa38880d6d7d..fdde4ea36583 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/CertificateMonitor.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/CertificateMonitor.java @@ -205,9 +205,10 @@ public class CertificateMonitor { dialogIntent.setComponent(targetInfo.getComponentName()); } + // Simple notification clicks are immutable PendingIntent notifyIntent = mInjector.pendingIntentGetActivityAsUser(userContext, 0, - dialogIntent, PendingIntent.FLAG_UPDATE_CURRENT, null, - UserHandle.of(parentUserId)); + dialogIntent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE, + null, UserHandle.of(parentUserId)); return new Notification.Builder(userContext, SystemNotificationChannels.SECURITY) .setSmallIcon(smallIconId) diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 36ff97446349..cd3d50d91afd 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -1569,10 +1569,15 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } /** - * Creates a new {@link CallerIdentity} object to represent the caller's identity. If no - * component name is provided, look up the component name and fill it in for the caller. + * Creates a new {@link CallerIdentity} object to represent the caller's identity, which should + * be an admin of a profile on the device. If no component name is provided, look up the + * component name and fill it in for the caller. + * + * Note: this method should only be called when the expected caller is an admin. + * + * @throws SecurityException if the caller is not an active admin. */ - private CallerIdentity getCallerIdentityOptionalAdmin(@Nullable ComponentName adminComponent) { + private CallerIdentity getAdminCallerIdentity(@Nullable ComponentName adminComponent) { if (adminComponent == null) { ActiveAdmin admin = getActiveAdminOfCaller(); if (admin != null) { @@ -1586,24 +1591,66 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { /** * Creates a new {@link CallerIdentity} object to represent the caller's identity. If no + * component name is provided, look up the component name and fill it in for the caller. + * + * Note: this method should only be called when the caller may not be an admin. If the caller + * is not an admin, the ComponentName in the returned identity will be null. + */ + private CallerIdentity getNonPrivilegedOrAdminCallerIdentity( + @Nullable ComponentName adminComponent) { + if (adminComponent == null) { + ActiveAdmin admin = getActiveAdminOfCaller(); + if (admin != null) { + adminComponent = admin.info.getComponent(); + } else { + return getCallerIdentity(); + } + } + return getCallerIdentity(adminComponent); + + } + + /** + * Creates a new {@link CallerIdentity} object to represent the caller's identity. If no * package name is provided, look up the package name and fill it in for the caller. + * + * Note: this method should only be called when the expected caller is an admin. + * + * @throws SecurityException if the caller is not an active admin. */ - private CallerIdentity getCallerIdentityOptionalPackage(@Nullable String callerPackage) { + private CallerIdentity getAdminCallerIdentityUsingPackage(@Nullable String callerPackage) { if (callerPackage == null) { ActiveAdmin admin = getActiveAdminOfCaller(); if (admin != null) { return getCallerIdentity(admin.info.getPackageName()); } throw new SecurityException("Caller is not an active admin"); - } else { - return getCallerIdentity(callerPackage); } + return getCallerIdentity(callerPackage); + } + + /** + * Creates a new {@link CallerIdentity} object to represent the caller's identity. If no + * package name is provided, look up the package name and fill it in for the caller. + */ + private CallerIdentity getNonPrivilegedOrAdminCallerIdentityUsingPackage( + @Nullable String callerPackage) { + if (callerPackage == null) { + ActiveAdmin admin = getActiveAdminOfCaller(); + if (admin != null) { + callerPackage = admin.info.getPackageName(); + } else { + return getCallerIdentity(); + } + } + return getCallerIdentity(callerPackage); } /** * Retrieves the active admin of the caller. This method should not be called directly and - * should only be called by {@link #getCallerIdentityOptionalAdmin} or - * {@link #getCallerIdentityOptionalPackage}. + * should only be called by {@link #getAdminCallerIdentity}, + * {@link #getNonPrivilegedOrAdminCallerIdentity}, {@link #getAdminCallerIdentityUsingPackage} + * or {@link #getNonPrivilegedOrAdminCallerIdentityUsingPackage}. */ private ActiveAdmin getActiveAdminOfCaller() { final int callerUid = mInjector.binderGetCallingUid(); @@ -2106,9 +2153,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { mInjector.binderWithCleanCallingIdentity(() -> { int affectedUserHandle = parent ? getProfileParentId(userHandle) : userHandle; AlarmManager am = mInjector.getAlarmManager(); + // Broadcast alarms sent by system are immutable PendingIntent pi = PendingIntent.getBroadcastAsUser(context, REQUEST_EXPIRE_PASSWORD, new Intent(ACTION_EXPIRED_PASSWORD_NOTIFICATION), - PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_UPDATE_CURRENT, + PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_UPDATE_CURRENT + | PendingIntent.FLAG_IMMUTABLE, UserHandle.of(affectedUserHandle)); am.cancel(pi); if (alarmTime != 0) { @@ -4348,12 +4397,13 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return mInjector.binderWithCleanCallingIdentity(() -> mUserManager.getUserInfo(userId)); } - private boolean setPasswordPrivileged(@NonNull String password, int flags, int callingUid) { + private boolean setPasswordPrivileged(@NonNull String password, int flags, + CallerIdentity caller) { // Only allow setting password on an unsecured user - if (isLockScreenSecureUnchecked(UserHandle.getUserId(callingUid))) { + if (isLockScreenSecureUnchecked(caller.getUserId())) { throw new SecurityException("Cannot change current password"); } - return resetPasswordInternal(password, 0, null, flags, callingUid); + return resetPasswordInternal(password, 0, null, flags, caller); } @Override @@ -4363,19 +4413,22 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return false; } if (password == null) password = ""; - final int callingUid = mInjector.binderGetCallingUid(); - final int userHandle = mInjector.userHandleGetCallingUserId(); + final CallerIdentity caller = getCallerIdentity(); + final int userHandle = caller.getUserId(); // As of R, only privlleged caller holding RESET_PASSWORD can call resetPassword() to // set password to an unsecured user. if (hasCallingPermission(permission.RESET_PASSWORD)) { - return setPasswordPrivileged(password, flags, callingUid); + return setPasswordPrivileged(password, flags, caller); } + // If caller has PO (or DO) throw or fail silently depending on its target SDK level. + Preconditions.checkCallAuthorization( + isDeviceOwner(caller) || isProfileOwner(caller), + String.format("UID %d is not a device or profile owner", caller.getUid())); + synchronized (getLockObject()) { - // If caller has PO (or DO) throw or fail silently depending on its target SDK level. - ActiveAdmin admin = getActiveAdminWithPolicyForUidLocked( - null, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER, callingUid); + ActiveAdmin admin = getDeviceOrProfileOwnerAdminLocked(caller.getUserId()); if (admin != null) { if (getTargetSdk(admin.info.getPackageName(), userHandle) < Build.VERSION_CODES.O) { Slog.e(LOG_TAG, "DPC can no longer call resetPassword()"); @@ -4397,7 +4450,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } private boolean resetPasswordInternal(String password, long tokenHandle, byte[] token, - int flags, int callingUid) { + int flags, CallerIdentity caller) { + final int callingUid = caller.getUid(); final int userHandle = UserHandle.getUserId(callingUid); synchronized (getLockObject()) { final PasswordMetrics minMetrics = getPasswordMinimumMetrics(userHandle); @@ -4426,7 +4480,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return false; } - boolean callerIsDeviceOwnerAdmin = isCallerDeviceOwner(callingUid); + boolean callerIsDeviceOwnerAdmin = isDeviceOwner(caller); boolean doNotAskCredentialsOnBoot = (flags & DevicePolicyManager.RESET_PASSWORD_DO_NOT_ASK_CREDENTIALS_ON_BOOT) != 0; if (callerIsDeviceOwnerAdmin && doNotAskCredentialsOnBoot) { @@ -5341,14 +5395,14 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { // Retrieve the user ID of the calling process. final int userId = caller.getUserId(); final boolean hasDoDelegation = !Collections.disjoint(scopes, DEVICE_OWNER_DELEGATIONS); + // Ensure calling process is device/profile owner. + if (hasDoDelegation) { + Preconditions.checkCallAuthorization(isDeviceOwner(caller)); + } else { + Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller)); + } + synchronized (getLockObject()) { - // Ensure calling process is device/profile owner. - if (hasDoDelegation) { - Preconditions.checkCallAuthorization(isDeviceOwner(caller)); - } else { - // TODO move whole condition out of synchronized block - getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); - } // Ensure the delegate is installed (skip this for DELEGATION_CERT_INSTALL in pre-N). if (shouldCheckIfDelegatePackageIsInstalled(delegatePackage, getTargetSdk(who.getPackageName(), userId), scopes)) { @@ -5469,11 +5523,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } // Retrieve the user ID of the calling process. - final int userId = mInjector.userHandleGetCallingUserId(); + final CallerIdentity caller = getCallerIdentity(who); + Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller)); synchronized (getLockObject()) { - // Ensure calling process is device/profile owner. - getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); - return getDelegatePackagesInternalLocked(scope, userId); + return getDelegatePackagesInternalLocked(scope, caller.getUserId()); } } @@ -5602,11 +5655,12 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { String delegatePackage, String scope) { Objects.requireNonNull(who, "ComponentName is null"); - final int userId = mInjector.userHandleGetCallingUserId(); + final CallerIdentity caller = getCallerIdentity(who); + // Ensure calling process is device/profile owner. + Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller)); + synchronized (getLockObject()) { - // Ensure calling process is device/profile owner. - getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); - final DevicePolicyData policy = getUserData(userId); + final DevicePolicyData policy = getUserData(caller.getUserId()); if (delegatePackage != null) { // Set package as a delegate for scope if it is not already one. @@ -5819,7 +5873,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { + "organization-owned device."); } if ((flags & WIPE_RESET_PROTECTION_DATA) != 0) { - Preconditions.checkCallAuthorization(isCallerDeviceOwner(caller.getUid()) + Preconditions.checkCallAuthorization(isDeviceOwner(caller) || calledByProfileOwnerOnOrgOwnedDevice, "Only device owners or profile owners of organization-owned device can set " + "WIPE_RESET_PROTECTION_DATA"); @@ -6023,7 +6077,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId"); - final CallerIdentity caller = getCallerIdentityOptionalAdmin(comp); + final CallerIdentity caller = getAdminCallerIdentity(comp); Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userHandle)); Preconditions.checkCallAuthorization(hasCallingOrSelfPermission(BIND_DEVICE_ADMIN)); @@ -6463,12 +6517,12 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId"); - final CallerIdentity caller = getCallerIdentityOptionalAdmin(who); + final CallerIdentity caller = getAdminCallerIdentity(who); Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userHandle)); synchronized (getLockObject()) { // Check for permissions if a particular caller is specified - if (who != null) { + if (caller.hasAdminComponent()) { // When checking for a single caller, status is based on caller's request ActiveAdmin ap = getActiveAdminUncheckedLocked(who, userHandle); return ap != null ? ap.encryptionRequested : false; @@ -6497,7 +6551,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId"); - final CallerIdentity caller = getCallerIdentityOptionalPackage(callerPackage); + final CallerIdentity caller = getAdminCallerIdentityUsingPackage(callerPackage); Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userHandle)); // It's not critical here, but let's make sure the package name is correct, in case @@ -6578,8 +6632,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } synchronized (getLockObject()) { - ActiveAdmin ap = getActiveAdminForCallerLocked(who, - DeviceAdminInfo.USES_POLICY_PROFILE_OWNER, parent); + ActiveAdmin ap = getParentOfAdminIfRequired(getProfileOwnerOrDeviceOwnerLocked(caller), + parent); if (ap.disableScreenCapture != disabled) { ap.disableScreenCapture = disabled; saveSettingsLocked(caller.getUserId()); @@ -7262,9 +7316,15 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { private boolean isDeviceOwner(CallerIdentity caller) { synchronized (getLockObject()) { - return mOwners.hasDeviceOwner() - && mOwners.getDeviceOwnerUserId() == caller.getUserId() - && mOwners.getDeviceOwnerComponent().equals(caller.getComponentName()); + if (!mOwners.hasDeviceOwner() || mOwners.getDeviceOwnerUserId() != caller.getUserId()) { + return false; + } + + if (caller.hasAdminComponent()) { + return mOwners.getDeviceOwnerComponent().equals(caller.getComponentName()); + } else { + return isUidDeviceOwnerLocked(caller.getUid()); + } } } @@ -7294,8 +7354,43 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { * @return true if {@code identity} is a profile owner, false otherwise. */ public boolean isProfileOwner(CallerIdentity caller) { - final ComponentName profileOwner = getProfileOwner(caller.getUserId()); - return profileOwner != null && profileOwner.equals(caller.getComponentName()); + synchronized (getLockObject()) { + final ComponentName profileOwner = getProfileOwner(caller.getUserId()); + // No profile owner. + if (profileOwner == null) { + return false; + } + // The admin ComponentName was specified, check it directly. + if (caller.hasAdminComponent()) { + return profileOwner.equals(caller.getComponentName()); + } else { + return isUidProfileOwnerLocked(caller.getUid()); + } + } + } + + /** + * Checks if the app uid provided is the profile owner. This method should only be called + * if no componentName is available. + * + * @param appUid UID of the caller. + * @return true if the caller is the profile owner + */ + private boolean isUidProfileOwnerLocked(int appUid) { + ensureLocked(); + + final int userId = UserHandle.getUserId(appUid); + final ComponentName profileOwnerComponent = mOwners.getProfileOwnerComponent(userId); + if (profileOwnerComponent == null) { + return false; + } + for (ActiveAdmin admin : getUserData(userId).mAdminList) { + final ComponentName currentAdminComponent = admin.info.getComponent(); + if (admin.getUid() == appUid && profileOwnerComponent.equals(currentAdminComponent)) { + return true; + } + } + return false; } private boolean hasProfileOwner(int userId) { @@ -7605,8 +7700,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { enforceUserUnlocked(userId); synchronized (getLockObject()) { // Check if this is the profile owner who is calling - final ActiveAdmin admin = - getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); + final ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(caller); mInjector.binderWithCleanCallingIdentity(() -> { clearProfileOwnerLocked(admin, userId); @@ -8321,8 +8415,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } private void enforceCanCallLockTaskLocked(ComponentName who) { - getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); - final int userId = mInjector.userHandleGetCallingUserId(); + final CallerIdentity caller = getAdminCallerIdentity(who); + Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller)); + + final int userId = caller.getUserId(); if (!canUserUseLockTaskLocked(userId)) { throw new SecurityException("User " + userId + " is not allowed to use lock task"); } @@ -8486,10 +8582,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { public void addPersistentPreferredActivity(ComponentName who, IntentFilter filter, ComponentName activity) { Objects.requireNonNull(who, "ComponentName is null"); - final int userHandle = UserHandle.getCallingUserId(); - synchronized (getLockObject()) { - getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); + final CallerIdentity caller = getCallerIdentity(who); + Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller)); + final int userHandle = caller.getUserId(); + synchronized (getLockObject()) { long id = mInjector.binderClearCallingIdentity(); try { mIPackageManager.addPersistentPreferredActivity(filter, activity, userHandle); @@ -8512,10 +8609,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @Override public void clearPackagePersistentPreferredActivities(ComponentName who, String packageName) { Objects.requireNonNull(who, "ComponentName is null"); - final int userHandle = UserHandle.getCallingUserId(); - synchronized (getLockObject()) { - getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); + final CallerIdentity caller = getCallerIdentity(who); + Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller)); + final int userHandle = caller.getUserId(); + synchronized (getLockObject()) { long id = mInjector.binderClearCallingIdentity(); try { mIPackageManager.clearPackagePersistentPreferredActivities(packageName, userHandle); @@ -8614,7 +8712,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { Objects.requireNonNull(agent, "agent null"); Preconditions.checkArgumentNonnegative(userHandle, "Invalid userId"); - final CallerIdentity caller = getCallerIdentityOptionalAdmin(admin); + final CallerIdentity caller = getAdminCallerIdentity(admin); Preconditions.checkCallAuthorization(hasFullCrossUsersPermission(caller, userHandle)); synchronized (getLockObject()) { @@ -8667,10 +8765,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @Override public void setRestrictionsProvider(ComponentName who, ComponentName permissionProvider) { Objects.requireNonNull(who, "ComponentName is null"); - synchronized (getLockObject()) { - getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); + final CallerIdentity caller = getCallerIdentity(who); + Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller)); - int userHandle = UserHandle.getCallingUserId(); + synchronized (getLockObject()) { + int userHandle = caller.getUserId(); DevicePolicyData userData = getUserData(userHandle); userData.mRestrictionsProvider = permissionProvider; saveSettingsLocked(userHandle); @@ -8689,10 +8788,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @Override public void addCrossProfileIntentFilter(ComponentName who, IntentFilter filter, int flags) { Objects.requireNonNull(who, "ComponentName is null"); - int callingUserId = UserHandle.getCallingUserId(); + final CallerIdentity caller = getCallerIdentity(who); + Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller)); + int callingUserId = caller.getUserId(); synchronized (getLockObject()) { - getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); - long id = mInjector.binderClearCallingIdentity(); try { UserInfo parent = mUserManager.getProfileParent(callingUserId); @@ -8738,9 +8837,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @Override public void clearCrossProfileIntentFilters(ComponentName who) { Objects.requireNonNull(who, "ComponentName is null"); - int callingUserId = UserHandle.getCallingUserId(); + final CallerIdentity caller = getCallerIdentity(who); + Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller)); + + int callingUserId = caller.getUserId(); synchronized (getLockObject()) { - getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); long id = mInjector.binderClearCallingIdentity(); try { UserInfo parent = mUserManager.getProfileParent(callingUserId); @@ -9415,10 +9516,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @Override public int logoutUser(ComponentName who) { Objects.requireNonNull(who, "ComponentName is null"); + final CallerIdentity caller = getCallerIdentity(who); + Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller)); - final int callingUserId = mInjector.userHandleGetCallingUserId(); + final int callingUserId = caller.getUserId(); synchronized (getLockObject()) { - getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); if (!isUserAffiliatedWithDeviceLocked(callingUserId)) { throw new SecurityException("Admin " + who + " is neither the device owner or affiliated user's profile owner."); @@ -9580,9 +9682,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { int userHandle = caller.getUserId(); synchronized (getLockObject()) { - final ActiveAdmin activeAdmin = - getActiveAdminForCallerLocked(who, - DeviceAdminInfo.USES_POLICY_PROFILE_OWNER, parent); + final ActiveAdmin activeAdmin = getParentOfAdminIfRequired( + getProfileOwnerOrDeviceOwnerLocked(caller), parent); if (isDeviceOwner(caller)) { if (!UserRestrictionsUtils.canDeviceOwnerChange(key)) { @@ -10499,19 +10600,17 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { public void setSystemSetting(ComponentName who, String setting, String value) { Objects.requireNonNull(who, "ComponentName is null"); Preconditions.checkStringNotEmpty(setting, "String setting is null or empty"); + final CallerIdentity caller = getCallerIdentity(who); + Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller)); synchronized (getLockObject()) { - getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); - if (!SYSTEM_SETTINGS_ALLOWLIST.contains(setting)) { throw new SecurityException(String.format( "Permission denial: device owners cannot update %1$s", setting)); } - final int callingUserId = mInjector.userHandleGetCallingUserId(); - mInjector.binderWithCleanCallingIdentity(() -> - mInjector.settingsSystemPutStringForUser(setting, value, callingUserId)); + mInjector.settingsSystemPutStringForUser(setting, value, caller.getUserId())); } } @@ -10596,8 +10695,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { Slog.wtf(LOG_TAG, "Failed to resolve intent for location settings"); } + // Simple notification clicks are immutable PendingIntent locationSettingsIntent = mInjector.pendingIntentGetActivityAsUser(mContext, 0, - intent, PendingIntent.FLAG_UPDATE_CURRENT, null, user); + intent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE, null, + user); Notification notification = new Notification.Builder(mContext, SystemNotificationChannels.DEVICE_ADMIN) .setSmallIcon(R.drawable.ic_info_outline) @@ -10657,11 +10758,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @Override public void setSecureSetting(ComponentName who, String setting, String value) { Objects.requireNonNull(who, "ComponentName is null"); - int callingUserId = mInjector.userHandleGetCallingUserId(); + final CallerIdentity caller = getCallerIdentity(who); + Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller)); + int callingUserId = caller.getUserId(); synchronized (getLockObject()) { - getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); - if (isDeviceOwner(who, callingUserId)) { if (!SECURE_SETTINGS_DEVICEOWNER_ALLOWLIST.contains(setting) && !isCurrentUserDemo()) { @@ -10753,8 +10854,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @Override public void setMasterVolumeMuted(ComponentName who, boolean on) { Objects.requireNonNull(who, "ComponentName is null"); + final CallerIdentity caller = getCallerIdentity(who); + Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller)); + synchronized (getLockObject()) { - getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); setUserRestriction(who, UserManager.DISALLOW_UNMUTE_DEVICE, on, /* parent */ false); DevicePolicyEventLogger .createEvent(DevicePolicyEnums.SET_MASTER_VOLUME_MUTED) @@ -10767,9 +10870,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @Override public boolean isMasterVolumeMuted(ComponentName who) { Objects.requireNonNull(who, "ComponentName is null"); - synchronized (getLockObject()) { - getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); + final CallerIdentity caller = getCallerIdentity(who); + Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller)); + synchronized (getLockObject()) { AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE); return audioManager.isMasterMute(); @@ -10778,13 +10882,13 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @Override public void setUserIcon(ComponentName who, Bitmap icon) { - synchronized (getLockObject()) { - Objects.requireNonNull(who, "ComponentName is null"); - getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); + Objects.requireNonNull(who, "ComponentName is null"); + final CallerIdentity caller = getCallerIdentity(who); + Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller)); - int userId = UserHandle.getCallingUserId(); + synchronized (getLockObject()) { mInjector.binderWithCleanCallingIdentity( - () -> mUserManagerInternal.setUserIcon(userId, icon)); + () -> mUserManagerInternal.setUserIcon(caller.getUserId(), icon)); } DevicePolicyEventLogger .createEvent(DevicePolicyEnums.SET_USER_ICON) @@ -10795,13 +10899,15 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @Override public boolean setKeyguardDisabled(ComponentName who, boolean disabled) { Objects.requireNonNull(who, "ComponentName is null"); - final int userId = mInjector.userHandleGetCallingUserId(); + final CallerIdentity caller = getCallerIdentity(who); + Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller)); + + final int userId = caller.getUserId(); synchronized (getLockObject()) { - getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); - if (!isUserAffiliatedWithDeviceLocked(userId)) { - throw new SecurityException("Admin " + who + - " is neither the device owner or affiliated user's profile owner."); - } + Preconditions.checkCallAuthorization(isUserAffiliatedWithDeviceLocked(userId), + String.format( + "Admin %s is neither the device owner or affiliated user's profile " + + "owner.", who)); } if (isManagedProfile(userId)) { throw new SecurityException("Managed profile cannot disable keyguard"); @@ -10834,13 +10940,14 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @Override public boolean setStatusBarDisabled(ComponentName who, boolean disabled) { - int userId = UserHandle.getCallingUserId(); + final CallerIdentity caller = getAdminCallerIdentity(who); + Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller)); + + int userId = caller.getUserId(); synchronized (getLockObject()) { - getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); - if (!isUserAffiliatedWithDeviceLocked(userId)) { - throw new SecurityException("Admin " + who + - " is neither the device owner or affiliated user's profile owner."); - } + Preconditions.checkCallAuthorization(isUserAffiliatedWithDeviceLocked(userId), + "Admin " + who + + " is neither the device owner or affiliated user's profile owner."); if (isManagedProfile(userId)) { throw new SecurityException("Managed profile cannot disable status bar"); } @@ -11581,32 +11688,24 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } /** - * Checks if the caller of the method is the device owner app. - * - * @param callerUid UID of the caller. - * @return true if the caller is the device owner app + * Checks if any of the packages associated with the UID of the app provided is that + * of the device owner. + * @param appUid UID of the app to check. + * @return {@code true} if any of the packages are the device owner, {@code false} otherwise. */ - @VisibleForTesting - boolean isCallerDeviceOwner(int callerUid) { - synchronized (getLockObject()) { - if (!mOwners.hasDeviceOwner()) { - return false; - } - if (UserHandle.getUserId(callerUid) != mOwners.getDeviceOwnerUserId()) { - return false; - } - final String deviceOwnerPackageName = mOwners.getDeviceOwnerComponent() - .getPackageName(); - try { - String[] pkgs = mInjector.getIPackageManager().getPackagesForUid(callerUid); - for (String pkg : pkgs) { - if (deviceOwnerPackageName.equals(pkg)) { - return true; - } - } - } catch (RemoteException e) { - return false; + private boolean isUidDeviceOwnerLocked(int appUid) { + ensureLocked(); + final String deviceOwnerPackageName = mOwners.getDeviceOwnerComponent() + .getPackageName(); + try { + String[] pkgs = mInjector.getIPackageManager().getPackagesForUid(appUid); + for (String pkg : pkgs) { + if (deviceOwnerPackageName.equals(pkg)) { + return true; } + } + } catch (RemoteException e) { + return false; } return false; } @@ -12586,9 +12685,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } final Set<String> affiliationIds = new ArraySet<>(ids); - final int callingUserId = mInjector.userHandleGetCallingUserId(); + final CallerIdentity caller = getAdminCallerIdentity(admin); + Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller)); + final int callingUserId = caller.getUserId(); + synchronized (getLockObject()) { - getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); getUserData(callingUserId).mAffiliationIds = affiliationIds; saveSettingsLocked(callingUserId); if (callingUserId != UserHandle.USER_SYSTEM && isDeviceOwner(admin, callingUserId)) { @@ -12614,10 +12715,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } Objects.requireNonNull(admin); + final CallerIdentity caller = getCallerIdentity(admin); + Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller)); + synchronized (getLockObject()) { - getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); - return new ArrayList<String>( - getUserData(mInjector.userHandleGetCallingUserId()).mAffiliationIds); + return new ArrayList<String>(getUserData(caller.getUserId()).mAffiliationIds); } } @@ -13127,11 +13229,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return Collections.emptyList(); } Objects.requireNonNull(admin); + final CallerIdentity caller = getCallerIdentity(admin); + Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller)); synchronized (getLockObject()) { - getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); - - final int callingUserId = mInjector.userHandleGetCallingUserId(); + final int callingUserId = caller.getUserId(); return mInjector.binderWithCleanCallingIdentity(() -> { ArrayList<UserHandle> targetUsers = new ArrayList<>(); if (!isDeviceOwner(admin, callingUserId)) { @@ -13447,8 +13549,9 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { final PackageManagerInternal pm = mInjector.getPackageManagerInternal(); final Intent intent = new Intent(DevicePolicyManager.ACTION_SHOW_DEVICE_MONITORING_DIALOG); intent.setPackage(pm.getSystemUiServiceComponent().getPackageName()); - final PendingIntent pendingIntent = PendingIntent.getBroadcastAsUser(mContext, 0, intent, 0, - UserHandle.CURRENT); + // Simple notification clicks are immutable + final PendingIntent pendingIntent = PendingIntent.getBroadcastAsUser(mContext, 0, intent, + PendingIntent.FLAG_IMMUTABLE, UserHandle.CURRENT); Notification notification = new Notification.Builder(mContext, SystemNotificationChannels.DEVICE_ADMIN) .setSmallIcon(R.drawable.ic_info_outline) @@ -13534,9 +13637,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { if (token == null || token.length < 32) { throw new IllegalArgumentException("token must be at least 32-byte long"); } + final CallerIdentity caller = getAdminCallerIdentity(admin); + Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller)); + synchronized (getLockObject()) { - final int userHandle = mInjector.userHandleGetCallingUserId(); - getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); + final int userHandle = caller.getUserId(); DevicePolicyData policy = getUserData(userHandle); return mInjector.binderWithCleanCallingIdentity(() -> { @@ -13556,9 +13661,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { if (!mHasFeature || !mLockPatternUtils.hasSecureLockScreen()) { return false; } + final CallerIdentity caller = getAdminCallerIdentity(admin); + Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller)); + synchronized (getLockObject()) { - final int userHandle = mInjector.userHandleGetCallingUserId(); - getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); + final int userHandle = caller.getUserId(); DevicePolicyData policy = getUserData(userHandle); if (policy.mPasswordTokenHandle != 0) { @@ -13579,11 +13686,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { if (!mHasFeature || !mLockPatternUtils.hasSecureLockScreen()) { return false; } - synchronized (getLockObject()) { - final int userHandle = mInjector.userHandleGetCallingUserId(); - getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); + final CallerIdentity caller = getAdminCallerIdentity(admin); + Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller)); - return isResetPasswordTokenActiveForUserLocked(userHandle); + synchronized (getLockObject()) { + return isResetPasswordTokenActiveForUserLocked(caller.getUserId()); } } @@ -13603,15 +13710,16 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return false; } Objects.requireNonNull(token); - synchronized (getLockObject()) { - final int userHandle = mInjector.userHandleGetCallingUserId(); - getActiveAdminForCallerLocked(admin, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); - DevicePolicyData policy = getUserData(userHandle); + final CallerIdentity caller = getAdminCallerIdentity(admin); + Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller)); + + synchronized (getLockObject()) { + DevicePolicyData policy = getUserData(caller.getUserId()); if (policy.mPasswordTokenHandle != 0) { final String password = passwordOrNull != null ? passwordOrNull : ""; return resetPasswordInternal(password, policy.mPasswordTokenHandle, token, - flags, mInjector.binderGetCallingUid()); + flags, caller); } else { Slog.w(LOG_TAG, "No saved token handle"); } @@ -13926,9 +14034,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @Override @Nullable public PersistableBundle getTransferOwnershipBundle() { + final CallerIdentity caller = getCallerIdentity(); + Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller)); + synchronized (getLockObject()) { - final int callingUserId = mInjector.userHandleGetCallingUserId(); - getActiveAdminForCallerLocked(null, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); + final int callingUserId = caller.getUserId(); final File bundleFile = new File( mInjector.environmentGetUserSystemDirectory(callingUserId), TRANSFER_OWNERSHIP_PARAMETERS_XML); @@ -14832,9 +14942,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { final AlarmManager am = mInjector.getAlarmManager(); final Intent intent = new Intent(ACTION_PROFILE_OFF_DEADLINE); intent.setPackage(mContext.getPackageName()); + // Broadcast alarms sent by system are immutable final PendingIntent pi = mInjector.pendingIntentGetBroadcast( mContext, REQUEST_PROFILE_OFF_DEADLINE, intent, - PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_UPDATE_CURRENT); + PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_UPDATE_CURRENT + | PendingIntent.FLAG_IMMUTABLE); if (alarmTime == 0) { Slog.i(LOG_TAG, "Profile off deadline alarm is removed."); @@ -14895,8 +15007,10 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { intent.setPackage(mContext.getPackageName()); intent.putExtra(Intent.EXTRA_USER_HANDLE, profileUserId); + // Simple notification action button clicks are immutable final PendingIntent pendingIntent = mInjector.pendingIntentGetBroadcast(mContext, - 0 /* requestCode */, intent, PendingIntent.FLAG_UPDATE_CURRENT); + 0 /* requestCode */, intent, + PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE); final String buttonText = mContext.getString(R.string.personal_apps_suspended_turn_profile_on); diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/RemoteBugreportManager.java b/services/devicepolicy/java/com/android/server/devicepolicy/RemoteBugreportManager.java index 46c9aab5bb97..543f3815454e 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/RemoteBugreportManager.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/RemoteBugreportManager.java @@ -137,8 +137,9 @@ public class RemoteBugreportManager { Slog.wtf(LOG_TAG, "Failed to resolve intent for remote bugreport dialog"); } + // Simple notification clicks are immutable final PendingIntent pendingDialogIntent = PendingIntent.getActivityAsUser(mContext, type, - dialogIntent, 0, null, UserHandle.CURRENT); + dialogIntent, PendingIntent.FLAG_IMMUTABLE, null, UserHandle.CURRENT); final Notification.Builder builder = new Notification.Builder(mContext, SystemNotificationChannels.DEVICE_ADMIN) @@ -158,12 +159,14 @@ public class RemoteBugreportManager { R.string.taking_remote_bugreport_notification_title)) .setProgress(0, 0, true); } else if (type == NOTIFICATION_BUGREPORT_FINISHED_NOT_ACCEPTED) { + // Simple notification action button clicks are immutable final PendingIntent pendingIntentAccept = PendingIntent.getBroadcast(mContext, NOTIFICATION_ID, new Intent(ACTION_BUGREPORT_SHARING_ACCEPTED), - PendingIntent.FLAG_CANCEL_CURRENT); + PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE); + // Simple notification action button clicks are immutable final PendingIntent pendingIntentDecline = PendingIntent.getBroadcast(mContext, NOTIFICATION_ID, new Intent(ACTION_BUGREPORT_SHARING_DECLINED), - PendingIntent.FLAG_CANCEL_CURRENT); + PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE); builder.addAction(new Notification.Action.Builder(null /* icon */, mContext.getString( R.string.decline_remote_bugreport_action), pendingIntentDecline).build()) .addAction(new Notification.Action.Builder(null /* icon */, mContext.getString( diff --git a/services/incremental/BinderIncrementalService.cpp b/services/incremental/BinderIncrementalService.cpp index 2f8825b064ce..a31aac96eb48 100644 --- a/services/incremental/BinderIncrementalService.cpp +++ b/services/incremental/BinderIncrementalService.cpp @@ -323,6 +323,22 @@ binder::Status BinderIncrementalService::unregisterLoadingProgressListener(int32 return ok(); } +binder::Status BinderIncrementalService::registerStorageHealthListener( + int32_t storageId, + const ::android::os::incremental::StorageHealthCheckParams& healthCheckParams, + const ::android::sp<IStorageHealthListener>& healthListener, bool* _aidl_return) { + *_aidl_return = mImpl.registerStorageHealthListener(storageId, + const_cast<StorageHealthCheckParams&&>( + healthCheckParams), + healthListener); + return ok(); +} + +binder::Status BinderIncrementalService::unregisterStorageHealthListener(int32_t storageId) { + mImpl.unregisterStorageHealthListener(storageId); + return ok(); +} + } // namespace android::os::incremental jlong Incremental_IncrementalService_Start(JNIEnv* env) { diff --git a/services/incremental/BinderIncrementalService.h b/services/incremental/BinderIncrementalService.h index 0a89166f4868..8afa0f7bb117 100644 --- a/services/incremental/BinderIncrementalService.h +++ b/services/incremental/BinderIncrementalService.h @@ -89,6 +89,11 @@ public: progressListener, bool* _aidl_return) final; binder::Status unregisterLoadingProgressListener(int32_t storageId, bool* _aidl_return) final; + binder::Status registerStorageHealthListener( + int32_t storageId, + const ::android::os::incremental::StorageHealthCheckParams& healthCheckParams, + const ::android::sp<IStorageHealthListener>& healthListener, bool* _aidl_return) final; + binder::Status unregisterStorageHealthListener(int32_t storageId) final; private: android::incremental::IncrementalService mImpl; diff --git a/services/incremental/IncrementalService.cpp b/services/incremental/IncrementalService.cpp index 5f145f33f628..599ac9344e73 100644 --- a/services/incremental/IncrementalService.cpp +++ b/services/incremental/IncrementalService.cpp @@ -1801,6 +1801,31 @@ bool IncrementalService::unregisterLoadingProgressListener(StorageId storage) { return removeTimedJobs(*mProgressUpdateJobQueue, storage); } +bool IncrementalService::registerStorageHealthListener( + StorageId storage, StorageHealthCheckParams&& healthCheckParams, + const StorageHealthListener& healthListener) { + DataLoaderStubPtr dataLoaderStub; + { + std::unique_lock l(mLock); + const auto& ifs = getIfsLocked(storage); + if (!ifs) { + return false; + } + dataLoaderStub = ifs->dataLoaderStub; + if (!dataLoaderStub) { + return false; + } + } + dataLoaderStub->setHealthListener(std::move(healthCheckParams), &healthListener); + return true; +} + +void IncrementalService::unregisterStorageHealthListener(StorageId storage) { + StorageHealthCheckParams invalidCheckParams; + invalidCheckParams.blockedTimeoutMs = -1; + registerStorageHealthListener(storage, std::move(invalidCheckParams), {}); +} + bool IncrementalService::perfLoggingEnabled() { static const bool enabled = base::GetBoolProperty("incremental.perflogging", false); return enabled; @@ -2137,6 +2162,19 @@ binder::Status IncrementalService::DataLoaderStub::onStatusChanged(MountId mount binder::Status IncrementalService::DataLoaderStub::reportStreamHealth(MountId mountId, int newStatus) { + if (!isValid()) { + return binder::Status:: + fromServiceSpecificError(-EINVAL, + "reportStreamHealth came to invalid DataLoaderStub"); + } + if (id() != mountId) { + LOG(ERROR) << "Mount ID mismatch: expected " << id() << ", but got: " << mountId; + return binder::Status::fromServiceSpecificError(-EPERM, "Mount ID mismatch."); + } + { + std::lock_guard lock(mMutex); + mStreamStatus = newStatus; + } return binder::Status::ok(); } @@ -2153,6 +2191,33 @@ void IncrementalService::DataLoaderStub::onHealthStatus(StorageHealthListener he } } +static int adjustHealthStatus(int healthStatus, int streamStatus) { + if (healthStatus == IStorageHealthListener::HEALTH_STATUS_OK) { + // everything is good; no need to change status + return healthStatus; + } + int newHeathStatus = healthStatus; + switch (streamStatus) { + case IDataLoaderStatusListener::STREAM_STORAGE_ERROR: + // storage is limited and storage not healthy + newHeathStatus = IStorageHealthListener::HEALTH_STATUS_UNHEALTHY_STORAGE; + break; + case IDataLoaderStatusListener::STREAM_INTEGRITY_ERROR: + // fall through + case IDataLoaderStatusListener::STREAM_SOURCE_ERROR: + // fall through + case IDataLoaderStatusListener::STREAM_TRANSPORT_ERROR: + if (healthStatus == IStorageHealthListener::HEALTH_STATUS_UNHEALTHY) { + newHeathStatus = IStorageHealthListener::HEALTH_STATUS_UNHEALTHY_TRANSPORT; + } + // pending/blocked status due to transportation issues is not regarded as unhealthy + break; + default: + break; + } + return newHeathStatus; +} + void IncrementalService::DataLoaderStub::updateHealthStatus(bool baseline) { LOG(DEBUG) << id() << ": updateHealthStatus" << (baseline ? " (baseline)" : ""); @@ -2232,6 +2297,8 @@ void IncrementalService::DataLoaderStub::updateHealthStatus(bool baseline) { checkBackAfter = unhealthyMonitoring; healthStatusToReport = IStorageHealthListener::HEALTH_STATUS_UNHEALTHY; } + // Adjust health status based on stream status + healthStatusToReport = adjustHealthStatus(healthStatusToReport, mStreamStatus); LOG(DEBUG) << id() << ": updateHealthStatus in " << double(checkBackAfter.count()) / 1000.0 << "secs"; mService.addTimedJob(*mService.mTimedQueue, id(), checkBackAfter, @@ -2321,6 +2388,18 @@ void IncrementalService::DataLoaderStub::unregisterFromPendingReads() { mService.mLooper->wake(); } +void IncrementalService::DataLoaderStub::setHealthListener( + StorageHealthCheckParams&& healthCheckParams, const StorageHealthListener* healthListener) { + std::lock_guard lock(mMutex); + mHealthCheckParams = std::move(healthCheckParams); + if (healthListener == nullptr) { + // reset listener and params + mHealthListener = {}; + } else { + mHealthListener = *healthListener; + } +} + void IncrementalService::DataLoaderStub::onDump(int fd) { dprintf(fd, " dataLoader: {\n"); dprintf(fd, " currentStatus: %d\n", mCurrentStatus); diff --git a/services/incremental/IncrementalService.h b/services/incremental/IncrementalService.h index 504c02a57b86..4c4b8bd1ba50 100644 --- a/services/incremental/IncrementalService.h +++ b/services/incremental/IncrementalService.h @@ -140,7 +140,10 @@ public: bool registerLoadingProgressListener(StorageId storage, const StorageLoadingProgressListener& progressListener); bool unregisterLoadingProgressListener(StorageId storage); - + bool registerStorageHealthListener(StorageId storage, + StorageHealthCheckParams&& healthCheckParams, + const StorageHealthListener& healthListener); + void unregisterStorageHealthListener(StorageId storage); RawMetadata getMetadata(StorageId storage, std::string_view path) const; RawMetadata getMetadata(StorageId storage, FileId node) const; @@ -197,6 +200,8 @@ private: MountId id() const { return mId.load(std::memory_order_relaxed); } const content::pm::DataLoaderParamsParcel& params() const { return mParams; } + void setHealthListener(StorageHealthCheckParams&& healthCheckParams, + const StorageHealthListener* healthListener); private: binder::Status onStatusChanged(MountId mount, int newStatus) final; @@ -251,6 +256,7 @@ private: BootClockTsUs kernelTsUs; } mHealthBase = {TimePoint::max(), kMaxBootClockTsUs}; StorageHealthCheckParams mHealthCheckParams; + int mStreamStatus = content::pm::IDataLoaderStatusListener::STREAM_HEALTHY; }; using DataLoaderStubPtr = sp<DataLoaderStub>; diff --git a/services/incremental/test/IncrementalServiceTest.cpp b/services/incremental/test/IncrementalServiceTest.cpp index aec9fa1c3277..867312e0eb2f 100644 --- a/services/incremental/test/IncrementalServiceTest.cpp +++ b/services/incremental/test/IncrementalServiceTest.cpp @@ -177,6 +177,18 @@ public: } return binder::Status::ok(); } + binder::Status storageError(int32_t id) { + if (mListener) { + mListener->reportStreamHealth(id, IDataLoaderStatusListener::STREAM_STORAGE_ERROR); + } + return binder::Status::ok(); + } + binder::Status transportError(int32_t id) { + if (mListener) { + mListener->reportStreamHealth(id, IDataLoaderStatusListener::STREAM_INTEGRITY_ERROR); + } + return binder::Status::ok(); + } int32_t setStorageParams(bool enableReadLogs) { int32_t result = -1; EXPECT_NE(mServiceConnector.get(), nullptr); @@ -1221,4 +1233,83 @@ TEST_F(IncrementalServiceTest, testRegisterLoadingProgressListenerFailsToGetProg EXPECT_CALL(*listenerMock, onStorageLoadingProgressChanged(_, _)).Times(0); mIncrementalService->registerLoadingProgressListener(storageId, listener); } + +TEST_F(IncrementalServiceTest, testRegisterStorageHealthListenerSuccess) { + mIncFs->openMountSuccess(); + sp<NiceMock<MockStorageHealthListener>> listener{new NiceMock<MockStorageHealthListener>}; + sp<NiceMock<MockStorageHealthListener>> newListener{new NiceMock<MockStorageHealthListener>}; + NiceMock<MockStorageHealthListener>* newListenerMock = newListener.get(); + + TemporaryDir tempDir; + int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), + IncrementalService::CreateOptions::CreateNew, + {}, StorageHealthCheckParams{}, listener); + ASSERT_GE(storageId, 0); + StorageHealthCheckParams newParams; + newParams.blockedTimeoutMs = 10000; + newParams.unhealthyTimeoutMs = 20000; + newParams.unhealthyMonitoringMs = 30000; + ASSERT_TRUE(mIncrementalService->registerStorageHealthListener(storageId, std::move(newParams), + newListener)); + + using MS = std::chrono::milliseconds; + using MCS = std::chrono::microseconds; + + const auto blockedTimeout = MS(newParams.blockedTimeoutMs); + const auto unhealthyTimeout = MS(newParams.unhealthyTimeoutMs); + + const uint64_t kFirstTimestampUs = 1000000000ll; + const uint64_t kBlockedTimestampUs = + kFirstTimestampUs - std::chrono::duration_cast<MCS>(blockedTimeout).count(); + const uint64_t kUnhealthyTimestampUs = + kFirstTimestampUs - std::chrono::duration_cast<MCS>(unhealthyTimeout).count(); + + // test that old listener was not called + EXPECT_CALL(*listener.get(), + onHealthStatus(_, IStorageHealthListener::HEALTH_STATUS_READS_PENDING)) + .Times(0); + EXPECT_CALL(*newListenerMock, + onHealthStatus(_, IStorageHealthListener::HEALTH_STATUS_READS_PENDING)) + .Times(1); + EXPECT_CALL(*newListenerMock, onHealthStatus(_, IStorageHealthListener::HEALTH_STATUS_BLOCKED)) + .Times(1); + EXPECT_CALL(*newListenerMock, + onHealthStatus(_, IStorageHealthListener::HEALTH_STATUS_UNHEALTHY_STORAGE)) + .Times(1); + EXPECT_CALL(*newListenerMock, + onHealthStatus(_, IStorageHealthListener::HEALTH_STATUS_UNHEALTHY_TRANSPORT)) + .Times(1); + mIncFs->waitForPendingReadsSuccess(kFirstTimestampUs); + mLooper->mCallback(-1, -1, mLooper->mCallbackData); + + ASSERT_EQ(IStorageHealthListener::HEALTH_STATUS_READS_PENDING, newListener->mStatus); + ASSERT_EQ(storageId, newListener->mStorageId); + + auto timedCallback = mTimedQueue->mWhat; + mTimedQueue->clearJob(storageId); + + // test when health status is blocked with transport error + mDataLoader->transportError(storageId); + mIncFs->waitForPendingReadsSuccess(kBlockedTimestampUs); + timedCallback(); + ASSERT_EQ(IStorageHealthListener::HEALTH_STATUS_BLOCKED, newListener->mStatus); + timedCallback = mTimedQueue->mWhat; + mTimedQueue->clearJob(storageId); + + // test when health status is blocked with storage error + mDataLoader->storageError(storageId); + mIncFs->waitForPendingReadsSuccess(kBlockedTimestampUs); + timedCallback(); + ASSERT_EQ(IStorageHealthListener::HEALTH_STATUS_UNHEALTHY_STORAGE, newListener->mStatus); + timedCallback = mTimedQueue->mWhat; + mTimedQueue->clearJob(storageId); + + // test when health status is unhealthy with transport error + mDataLoader->transportError(storageId); + mIncFs->waitForPendingReadsSuccess(kUnhealthyTimestampUs); + timedCallback(); + ASSERT_EQ(IStorageHealthListener::HEALTH_STATUS_UNHEALTHY_TRANSPORT, newListener->mStatus); + mTimedQueue->clearJob(storageId); +} + } // namespace android::os::incremental diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 275d8f169ca1..975e2265b60c 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -47,7 +47,9 @@ import android.database.sqlite.SQLiteCompatibilityWalFlags; import android.database.sqlite.SQLiteGlobal; import android.graphics.GraphicsStatsService; import android.hardware.display.DisplayManagerInternal; +import android.net.ConnectivityManager; import android.net.ConnectivityModuleConnector; +import android.net.IConnectivityManager; import android.net.NetworkStackClient; import android.os.BaseBundle; import android.os.Binder; @@ -103,7 +105,6 @@ import com.android.server.camera.CameraServiceProxy; import com.android.server.clipboard.ClipboardService; import com.android.server.compat.PlatformCompat; import com.android.server.compat.PlatformCompatNative; -import com.android.server.connectivity.IpConnectivityMetrics; import com.android.server.contentcapture.ContentCaptureManagerInternal; import com.android.server.coverage.CoverageService; import com.android.server.devicepolicy.DevicePolicyManagerService; @@ -320,6 +321,10 @@ public final class SystemServer { "com.android.server.media.MediaSessionService"; private static final String MEDIA_RESOURCE_MONITOR_SERVICE_CLASS = "com.android.server.media.MediaResourceMonitorService"; + private static final String CONNECTIVITY_SERVICE_INITIALIZER_CLASS = + "com.android.server.ConnectivityServiceInitializer"; + private static final String IP_CONNECTIVITY_METRICS_CLASS = + "com.android.server.connectivity.IpConnectivityMetrics"; private static final String TETHERING_CONNECTOR_CLASS = "android.net.ITetheringConnector"; @@ -1038,7 +1043,7 @@ public final class SystemServer { IpSecService ipSecService = null; NetworkStatsService networkStats = null; NetworkPolicyManagerService networkPolicy = null; - ConnectivityService connectivity = null; + IConnectivityManager connectivity = null; NsdService serviceDiscovery = null; WindowManagerService wm = null; SerialService serial = null; @@ -1228,7 +1233,7 @@ public final class SystemServer { } t.traceBegin("IpConnectivityMetrics"); - mSystemServiceManager.startService(IpConnectivityMetrics.class); + mSystemServiceManager.startService(IP_CONNECTIVITY_METRICS_CLASS); t.traceEnd(); t.traceBegin("NetworkWatchlistService"); @@ -1572,16 +1577,15 @@ public final class SystemServer { } t.traceBegin("StartConnectivityService"); - try { - connectivity = new ConnectivityService( - context, networkManagement, networkStats, networkPolicy); - ServiceManager.addService(Context.CONNECTIVITY_SERVICE, connectivity, - /* allowIsolated= */ false, - DUMP_FLAG_PRIORITY_HIGH | DUMP_FLAG_PRIORITY_NORMAL); - networkPolicy.bindConnectivityManager(connectivity); - } catch (Throwable e) { - reportWtf("starting Connectivity Service", e); - } + // This has to be called after NetworkManagementService, NetworkStatsService + // and NetworkPolicyManager because ConnectivityService needs to take these + // services to initialize. + // TODO: Dynamically load service-connectivity.jar by using startServiceFromJar. + mSystemServiceManager.startService(CONNECTIVITY_SERVICE_INITIALIZER_CLASS); + connectivity = IConnectivityManager.Stub.asInterface( + ServiceManager.getService(Context.CONNECTIVITY_SERVICE)); + // TODO: Use ConnectivityManager instead of ConnectivityService. + networkPolicy.bindConnectivityManager(connectivity); t.traceEnd(); t.traceBegin("StartNsdService"); @@ -2270,7 +2274,6 @@ public final class SystemServer { final NetworkManagementService networkManagementF = networkManagement; final NetworkStatsService networkStatsF = networkStats; final NetworkPolicyManagerService networkPolicyF = networkPolicy; - final ConnectivityService connectivityF = connectivity; final CountryDetectorService countryDetectorF = countryDetector; final NetworkTimeUpdateService networkTimeUpdaterF = networkTimeUpdater; final InputManagerService inputManagerF = inputManager; @@ -2279,6 +2282,8 @@ public final class SystemServer { final MmsServiceBroker mmsServiceF = mmsService; final IpSecService ipSecServiceF = ipSecService; final WindowManagerService windowManagerF = wm; + final ConnectivityManager connectivityF = (ConnectivityManager) + context.getSystemService(Context.CONNECTIVITY_SERVICE); // We now tell the activity manager it is okay to run third party // code. It will call back into us once it has gotten to the state diff --git a/services/net/TEST_MAPPING b/services/net/TEST_MAPPING new file mode 100644 index 000000000000..7025dd178e0f --- /dev/null +++ b/services/net/TEST_MAPPING @@ -0,0 +1,7 @@ +{ + "imports": [ + { + "path": "frameworks/base/core/java/android/net" + } + ] +}
\ No newline at end of file diff --git a/services/people/java/com/android/server/people/PeopleService.java b/services/people/java/com/android/server/people/PeopleService.java index 91cb481a3b23..7c3b7a67178e 100644 --- a/services/people/java/com/android/server/people/PeopleService.java +++ b/services/people/java/com/android/server/people/PeopleService.java @@ -122,7 +122,7 @@ public class PeopleService extends SystemService { @Override public ParceledListSlice<ConversationChannel> getRecentConversations() { - enforceSystemOrRoot("get recent conversations"); + enforceSystemRootOrSystemUI(getContext(), "get recent conversations"); return new ParceledListSlice<>( mDataManager.getRecentConversations( Binder.getCallingUserHandle().getIdentifier())); diff --git a/services/people/java/com/android/server/people/data/DataManager.java b/services/people/java/com/android/server/people/data/DataManager.java index 87f2c581ef8f..7803e78eee34 100644 --- a/services/people/java/com/android/server/people/data/DataManager.java +++ b/services/people/java/com/android/server/people/data/DataManager.java @@ -1037,14 +1037,15 @@ public class DataManager { * A {@link Runnable} that queries the Usage Stats Service for recent events for a specified * user. */ - private class UsageStatsQueryRunnable implements Runnable { + private class UsageStatsQueryRunnable implements Runnable, + UsageStatsQueryHelper.EventListener { private final UsageStatsQueryHelper mUsageStatsQueryHelper; private long mLastEventTimestamp; private UsageStatsQueryRunnable(int userId) { mUsageStatsQueryHelper = mInjector.createUsageStatsQueryHelper(userId, - (packageName) -> getPackage(packageName, userId)); + (packageName) -> getPackage(packageName, userId), this); mLastEventTimestamp = System.currentTimeMillis() - QUERY_EVENTS_MAX_AGE_MS; } @@ -1054,6 +1055,17 @@ public class DataManager { mLastEventTimestamp = mUsageStatsQueryHelper.getLastEventTimestamp(); } } + + @Override + public void onEvent(PackageData packageData, ConversationInfo conversationInfo, + Event event) { + if (event.getType() == Event.TYPE_IN_APP_CONVERSATION) { + ConversationInfo updated = new ConversationInfo.Builder(conversationInfo) + .setLastEventTimestamp(event.getTimestamp()) + .build(); + packageData.getConversationStore().addOrUpdate(updated); + } + } } /** A {@link BroadcastReceiver} that receives the intents for a specified user. */ @@ -1135,8 +1147,9 @@ public class DataManager { } UsageStatsQueryHelper createUsageStatsQueryHelper(@UserIdInt int userId, - Function<String, PackageData> packageDataGetter) { - return new UsageStatsQueryHelper(userId, packageDataGetter); + Function<String, PackageData> packageDataGetter, + UsageStatsQueryHelper.EventListener eventListener) { + return new UsageStatsQueryHelper(userId, packageDataGetter, eventListener); } } } diff --git a/services/people/java/com/android/server/people/data/UsageStatsQueryHelper.java b/services/people/java/com/android/server/people/data/UsageStatsQueryHelper.java index d008b72c6bad..aefb6e362255 100644 --- a/services/people/java/com/android/server/people/data/UsageStatsQueryHelper.java +++ b/services/people/java/com/android/server/people/data/UsageStatsQueryHelper.java @@ -43,17 +43,24 @@ class UsageStatsQueryHelper { private final Function<String, PackageData> mPackageDataGetter; // Activity name -> Conversation start event (LOCUS_ID_SET) private final Map<ComponentName, UsageEvents.Event> mConvoStartEvents = new ArrayMap<>(); + private final EventListener mEventListener; private long mLastEventTimestamp; + interface EventListener { + void onEvent(PackageData packageData, ConversationInfo conversationInfo, Event event); + } + /** * @param userId The user whose events are to be queried. * @param packageDataGetter The function to get {@link PackageData} with a package name. + * @param eventListener A listener that listens to the new event. */ UsageStatsQueryHelper(@UserIdInt int userId, - Function<String, PackageData> packageDataGetter) { + Function<String, PackageData> packageDataGetter, EventListener eventListener) { mUsageStatsManagerInternal = getUsageStatsManagerInternal(); mUserId = userId; mPackageDataGetter = packageDataGetter; + mEventListener = eventListener; } /** @@ -198,21 +205,27 @@ class UsageStatsQueryHelper { } private void addEventByShortcutId(PackageData packageData, String shortcutId, Event event) { - if (packageData.getConversationStore().getConversation(shortcutId) == null) { + ConversationInfo conversationInfo = + packageData.getConversationStore().getConversation(shortcutId); + if (conversationInfo == null) { return; } EventHistoryImpl eventHistory = packageData.getEventStore().getOrCreateEventHistory( EventStore.CATEGORY_SHORTCUT_BASED, shortcutId); eventHistory.addEvent(event); + mEventListener.onEvent(packageData, conversationInfo, event); } private void addEventByLocusId(PackageData packageData, LocusId locusId, Event event) { - if (packageData.getConversationStore().getConversationByLocusId(locusId) == null) { + ConversationInfo conversationInfo = + packageData.getConversationStore().getConversationByLocusId(locusId); + if (conversationInfo == null) { return; } EventHistoryImpl eventHistory = packageData.getEventStore().getOrCreateEventHistory( EventStore.CATEGORY_LOCUS_ID_BASED, locusId.getId()); eventHistory.addEvent(event); + mEventListener.onEvent(packageData, conversationInfo, event); } private static UsageStatsManagerInternal getUsageStatsManagerInternal() { diff --git a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java index ddd1f7568224..d14ed5a15cf9 100644 --- a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java +++ b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java @@ -29,6 +29,8 @@ import android.os.Looper; import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemProperties; +import android.os.UpdateEngine; +import android.os.UpdateEngineCallback; import android.util.Log; import com.android.server.IoThread; @@ -198,6 +200,7 @@ public final class ProfcollectForwardingService extends SystemService { // Event observers private void registerObservers() { registerAppLaunchObserver(); + registerOTAObserver(); } private final AppLaunchObserver mAppLaunchObserver = new AppLaunchObserver(); @@ -261,4 +264,33 @@ public final class ProfcollectForwardingService extends SystemService { // Ignored } } + + private void registerOTAObserver() { + UpdateEngine updateEngine = new UpdateEngine(); + updateEngine.bind(new UpdateEngineCallback() { + @Override + public void onStatusUpdate(int status, float percent) { + if (status == UpdateEngine.UpdateStatusConstants.UPDATED_NEED_REBOOT) { + packProfileReport(); + } + } + + @Override + public void onPayloadApplicationComplete(int errorCode) { + // Ignored + } + }); + } + + private void packProfileReport() { + if (mIProfcollect == null) { + return; + } + + try { + mIProfcollect.CreateProfileReport(); + } catch (RemoteException e) { + Log.e(LOG_TAG, e.getMessage()); + } + } } diff --git a/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt b/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt index 09552082e4af..27b07c76fb0d 100644 --- a/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt +++ b/services/tests/PackageManagerComponentOverrideTests/src/com/android/server/pm/test/override/PackageManagerComponentLabelIconOverrideTest.kt @@ -45,6 +45,7 @@ import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.Parameterized +import org.mockito.Mockito import org.mockito.Mockito.any import org.mockito.Mockito.anyBoolean import org.mockito.Mockito.anyInt @@ -341,7 +342,8 @@ class PackageManagerComponentLabelIconOverrideTest { whenever(this.userManagerService) { mockUserManagerService } whenever(this.permissionManagerServiceInternal) { mockPermissionManagerService } whenever(this.settings) { mockSettings } - whenever(this.activityTaskManagerInternal) { mockActivityTaskManager } + whenever(this.getLocalService(ActivityTaskManagerInternal::class.java)) { + mockActivityTaskManager} whenever(this.appsFilter) { mockAppsFilter } whenever(this.context) { mockContext } } diff --git a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java index d61783ef59d2..a5f083477863 100644 --- a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java @@ -48,10 +48,13 @@ import static com.android.server.alarm.AlarmManagerService.Constants.KEY_MIN_INT import static com.android.server.alarm.AlarmManagerService.IS_WAKEUP_MASK; import static com.android.server.alarm.AlarmManagerService.TIME_CHANGED_MASK; import static com.android.server.alarm.AlarmManagerService.WORKING_INDEX; +import static com.android.server.alarm.Constants.TEST_CALLING_PACKAGE; +import static com.android.server.alarm.Constants.TEST_CALLING_UID; 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 static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyLong; @@ -82,6 +85,7 @@ import android.os.UserHandle; import android.platform.test.annotations.Presubmit; import android.provider.DeviceConfig; import android.provider.Settings; +import android.util.IndentingPrintWriter; import android.util.Log; import android.util.SparseArray; @@ -108,6 +112,8 @@ import org.mockito.MockitoSession; import org.mockito.quality.Strictness; import org.mockito.stubbing.Answer; +import java.io.PrintWriter; +import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.HashSet; import java.util.concurrent.Executor; @@ -116,9 +122,7 @@ import java.util.concurrent.Executor; @RunWith(AndroidJUnit4.class) public class AlarmManagerServiceTest { private static final String TAG = AlarmManagerServiceTest.class.getSimpleName(); - private static final String TEST_CALLING_PACKAGE = "com.android.framework.test-package"; private static final int SYSTEM_UI_UID = 12345; - private static final int TEST_CALLING_UID = 67890; private static final int TEST_CALLING_USER = UserHandle.getUserId(TEST_CALLING_UID); private long mAppStandbyWindow; @@ -350,19 +354,31 @@ public class AlarmManagerServiceTest { } private void setTestAlarm(int type, long triggerTime, PendingIntent operation) { - setTestAlarm(type, triggerTime, operation, 0, TEST_CALLING_UID); + setTestAlarm(type, triggerTime, operation, 0, AlarmManager.FLAG_STANDALONE, + TEST_CALLING_UID); } private void setRepeatingTestAlarm(int type, long firstTrigger, long interval, PendingIntent pi) { - setTestAlarm(type, firstTrigger, pi, interval, TEST_CALLING_UID); + setTestAlarm(type, firstTrigger, pi, interval, AlarmManager.FLAG_STANDALONE, + TEST_CALLING_UID); + } + + private void setIdleUntilAlarm(int type, long triggerTime, PendingIntent pi) { + setTestAlarm(type, triggerTime, pi, 0, AlarmManager.FLAG_IDLE_UNTIL, TEST_CALLING_UID); + } + + private void setWakeFromIdle(int type, long triggerTime, PendingIntent pi) { + // Note: Only alarm clock alarms are allowed to include this flag in the actual service. + // But this is a unit test so we'll only test the flag for granularity and convenience. + setTestAlarm(type, triggerTime, pi, 0, + AlarmManager.FLAG_WAKE_FROM_IDLE | AlarmManager.FLAG_STANDALONE, TEST_CALLING_UID); } private void setTestAlarm(int type, long triggerTime, PendingIntent operation, long interval, - int callingUid) { - mService.setImpl(type, triggerTime, AlarmManager.WINDOW_EXACT, interval, - operation, null, "test", AlarmManager.FLAG_STANDALONE, null, null, - callingUid, TEST_CALLING_PACKAGE); + int flags, int callingUid) { + mService.setImpl(type, triggerTime, AlarmManager.WINDOW_EXACT, interval, operation, null, + "test", flags, null, null, callingUid, TEST_CALLING_PACKAGE); } private void setTestAlarmWithListener(int type, long triggerTime, IAlarmListener listener) { @@ -1002,7 +1018,7 @@ public class AlarmManagerServiceTest { for (int i = 0; i < numAlarms; i++) { int mockUid = UserHandle.getUid(mockUserId, 1234 + i); setTestAlarm(ELAPSED_REALTIME, mNowElapsedTest + i + 10, - getNewMockPendingIntent(mockUid), 0, mockUid); + getNewMockPendingIntent(mockUid), 0, AlarmManager.FLAG_STANDALONE, mockUid); } assertEquals(numAlarms, mService.mAlarmsPerUid.size()); mService.removeUserLocked(mockUserId); @@ -1142,6 +1158,116 @@ public class AlarmManagerServiceTest { } } + @Test + public void singleIdleUntil() { + doReturn(0).when(mService).fuzzForDuration(anyLong()); + + final PendingIntent idleUntilPi6 = getNewMockPendingIntent(); + setIdleUntilAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 6, idleUntilPi6); + + assertTrue(mService.mPendingIdleUntil.matches(idleUntilPi6, null)); + assertEquals(mNowElapsedTest + 6, mTestTimer.getElapsed()); + assertEquals(mNowElapsedTest + 6, mService.mPendingIdleUntil.getWhenElapsed()); + + final PendingIntent idleUntilPi2 = getNewMockPendingIntent(); + setIdleUntilAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 2, idleUntilPi2); + + // The same mPendingIdleUntil should get updated, even with a different PendingIntent. + assertTrue(mService.mPendingIdleUntil.matches(idleUntilPi2, null)); + assertEquals(mNowElapsedTest + 2, mTestTimer.getElapsed()); + assertEquals(1, mService.mAlarmStore.size()); + + final PendingIntent idleUntilPi10 = getNewMockPendingIntent(); + setIdleUntilAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 10, idleUntilPi10); + + // The same thing should happen even when the new alarm is in farther in the future. + assertTrue(mService.mPendingIdleUntil.matches(idleUntilPi10, null)); + assertEquals(mNowElapsedTest + 10, mTestTimer.getElapsed()); + assertEquals(1, mService.mAlarmStore.size()); + } + + @Test + public void nextWakeFromIdle() throws Exception { + assertNull(mService.mNextWakeFromIdle); + + final PendingIntent wakeFromIdle6 = getNewMockPendingIntent(); + final long trigger6 = mNowElapsedTest + 6; + setWakeFromIdle(ELAPSED_REALTIME_WAKEUP, trigger6, wakeFromIdle6); + + assertTrue(mService.mNextWakeFromIdle.matches(wakeFromIdle6, null)); + assertEquals(trigger6, mService.mNextWakeFromIdle.getWhenElapsed()); + assertEquals(trigger6, mTestTimer.getElapsed()); + + final PendingIntent wakeFromIdle10 = getNewMockPendingIntent(); + final long trigger10 = mNowElapsedTest + 10; + setWakeFromIdle(ELAPSED_REALTIME_WAKEUP, trigger10, wakeFromIdle10); + + // mNextWakeFromIdle should not get updated. + assertTrue(mService.mNextWakeFromIdle.matches(wakeFromIdle6, null)); + assertEquals(trigger6, mTestTimer.getElapsed()); + assertEquals(trigger6, mService.mNextWakeFromIdle.getWhenElapsed()); + + final PendingIntent wakeFromIdle3 = getNewMockPendingIntent(); + final long trigger3 = mNowElapsedTest + 3; + setWakeFromIdle(ELAPSED_REALTIME_WAKEUP, trigger3, wakeFromIdle3); + + // mNextWakeFromIdle should always reflect the next earliest wake_from_idle alarm. + assertTrue(mService.mNextWakeFromIdle.matches(wakeFromIdle3, null)); + assertEquals(trigger3, mTestTimer.getElapsed()); + assertEquals(trigger3, mService.mNextWakeFromIdle.getWhenElapsed()); + + mNowElapsedTest = trigger3; + mTestTimer.expire(); + + assertTrue(mService.mNextWakeFromIdle.matches(wakeFromIdle6, null)); + assertEquals(trigger6, mTestTimer.getElapsed()); + assertEquals(trigger6, mService.mNextWakeFromIdle.getWhenElapsed()); + + mService.removeLocked(wakeFromIdle6, null); + + assertTrue(mService.mNextWakeFromIdle.matches(wakeFromIdle10, null)); + assertEquals(trigger10, mTestTimer.getElapsed()); + assertEquals(trigger10, mService.mNextWakeFromIdle.getWhenElapsed()); + + mService.removeLocked(wakeFromIdle10, null); + assertNull(mService.mNextWakeFromIdle); + } + + @Test + public void idleUntilBeforeWakeFromIdle() { + doReturn(0).when(mService).fuzzForDuration(anyLong()); + + final PendingIntent idleUntilPi = getNewMockPendingIntent(); + final long requestedIdleUntil = mNowElapsedTest + 10; + setIdleUntilAlarm(ELAPSED_REALTIME_WAKEUP, requestedIdleUntil, idleUntilPi); + + assertEquals(requestedIdleUntil, mService.mPendingIdleUntil.getWhenElapsed()); + + final PendingIntent wakeFromIdle5 = getNewMockPendingIntent(); + setWakeFromIdle(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 5, wakeFromIdle5); + assertEquals(mNowElapsedTest + 5, mService.mPendingIdleUntil.getWhenElapsed()); + + final PendingIntent wakeFromIdle8 = getNewMockPendingIntent(); + setWakeFromIdle(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 8, wakeFromIdle8); + assertEquals(mNowElapsedTest + 5, mService.mPendingIdleUntil.getWhenElapsed()); + + final PendingIntent wakeFromIdle12 = getNewMockPendingIntent(); + setWakeFromIdle(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 12, wakeFromIdle12); + assertEquals(mNowElapsedTest + 5, mService.mPendingIdleUntil.getWhenElapsed()); + + mService.removeLocked(wakeFromIdle5, null); + assertEquals(mNowElapsedTest + 8, mService.mPendingIdleUntil.getWhenElapsed()); + + mService.removeLocked(wakeFromIdle8, null); + assertEquals(requestedIdleUntil, mService.mPendingIdleUntil.getWhenElapsed()); + + mService.removeLocked(idleUntilPi, null); + assertNull(mService.mPendingIdleUntil); + + setIdleUntilAlarm(ELAPSED_REALTIME_WAKEUP, mNowElapsedTest + 15, idleUntilPi); + assertEquals(mNowElapsedTest + 12, mService.mPendingIdleUntil.getWhenElapsed()); + } + @After public void tearDown() { if (mMockingSession != null) { @@ -1149,4 +1275,12 @@ public class AlarmManagerServiceTest { } LocalServices.removeServiceForTest(AlarmManagerInternal.class); } + + private void dumpAllAlarms(String tag, ArrayList<Alarm> alarms) { + System.out.println(tag + ": "); + IndentingPrintWriter ipw = new IndentingPrintWriter(new PrintWriter(System.out)); + AlarmManagerService.dumpAlarmList(ipw, alarms, mNowElapsedTest, + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS")); + ipw.close(); + } } diff --git a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmStoreTest.java b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmStoreTest.java index 9e43b4ab9b5a..f0490ce469dd 100644 --- a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmStoreTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmStoreTest.java @@ -16,6 +16,9 @@ package com.android.server.alarm; +import static com.android.server.alarm.Constants.TEST_CALLING_PACKAGE; +import static com.android.server.alarm.Constants.TEST_CALLING_UID; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.mock; @@ -35,9 +38,6 @@ import java.util.ArrayList; @Presubmit @RunWith(AndroidJUnit4.class) public class AlarmStoreTest { - private static final int TEST_CALLING_UID = 12345; - private static final String TEST_CALLING_PACKAGE = "android.alarm.unit.test"; - private AlarmStore mAlarmStore; @Before @@ -45,22 +45,22 @@ public class AlarmStoreTest { mAlarmStore = new BatchingAlarmStore(null); } - private static Alarm createAlarm(long whenElapsed, long windowLength, PendingIntent mockPi, + private static Alarm createAlarm(long whenElapsed, long windowLength, AlarmManager.AlarmClockInfo alarmClock) { - return createAlarm(AlarmManager.ELAPSED_REALTIME, whenElapsed, windowLength, mockPi, + return createAlarm(AlarmManager.ELAPSED_REALTIME, whenElapsed, windowLength, alarmClock); } private static Alarm createWakeupAlarm(long whenElapsed, long windowLength, - PendingIntent mockPi, AlarmManager.AlarmClockInfo alarmClock) { - return createAlarm(AlarmManager.ELAPSED_REALTIME_WAKEUP, whenElapsed, windowLength, mockPi, + AlarmManager.AlarmClockInfo alarmClock) { + return createAlarm(AlarmManager.ELAPSED_REALTIME_WAKEUP, whenElapsed, windowLength, alarmClock); } private static Alarm createAlarm(int type, long whenElapsed, long windowLength, - PendingIntent mockPi, AlarmManager.AlarmClockInfo alarmClock) { - return new Alarm(type, whenElapsed, whenElapsed, windowLength, whenElapsed + windowLength, - 0, mockPi, null, null, null, 0, alarmClock, TEST_CALLING_UID, TEST_CALLING_PACKAGE); + AlarmManager.AlarmClockInfo alarmClock) { + return new Alarm(type, whenElapsed, whenElapsed, windowLength, 0, mock(PendingIntent.class), + null, null, null, 0, alarmClock, TEST_CALLING_UID, TEST_CALLING_PACKAGE); } private void addAlarmsToStore(Alarm... alarms) { @@ -71,11 +71,11 @@ public class AlarmStoreTest { @Test public void add() { - final Alarm a1 = createAlarm(1, 0, mock(PendingIntent.class), null); + final Alarm a1 = createAlarm(1, 0, null); mAlarmStore.add(a1); assertEquals(1, mAlarmStore.size()); - final Alarm a2 = createAlarm(2, 0, mock(PendingIntent.class), null); + final Alarm a2 = createAlarm(2, 0, null); mAlarmStore.add(a2); assertEquals(2, mAlarmStore.size()); @@ -86,17 +86,17 @@ public class AlarmStoreTest { @Test public void remove() { - final Alarm a1 = createAlarm(1, 0, mock(PendingIntent.class), null); - final Alarm a2 = createAlarm(2, 0, mock(PendingIntent.class), null); - final Alarm a5 = createAlarm(5, 0, mock(PendingIntent.class), null); + final Alarm a1 = createAlarm(1, 0, null); + final Alarm a2 = createAlarm(2, 0, null); + final Alarm a5 = createAlarm(5, 0, null); addAlarmsToStore(a1, a2, a5); - ArrayList<Alarm> removed = mAlarmStore.remove(a -> (a.whenElapsed < 4)); + ArrayList<Alarm> removed = mAlarmStore.remove(a -> (a.getWhenElapsed() < 4)); assertEquals(2, removed.size()); assertEquals(1, mAlarmStore.size()); assertTrue(removed.contains(a1) && removed.contains(a2)); - final Alarm a8 = createAlarm(8, 0, mock(PendingIntent.class), null); + final Alarm a8 = createAlarm(8, 0, null); addAlarmsToStore(a8, a2, a1); removed = mAlarmStore.remove(unused -> false); @@ -110,10 +110,10 @@ public class AlarmStoreTest { @Test public void removePendingAlarms() { - final Alarm a1_11 = createAlarm(1, 10, mock(PendingIntent.class), null); - final Alarm a2_5 = createAlarm(2, 3, mock(PendingIntent.class), null); - final Alarm a6_9 = createAlarm(6, 3, mock(PendingIntent.class), null); - addAlarmsToStore(a2_5, a6_9, a1_11); + final Alarm a1to11 = createAlarm(1, 10, null); + final Alarm a2to5 = createAlarm(2, 3, null); + final Alarm a6to9 = createAlarm(6, 3, null); + addAlarmsToStore(a2to5, a6to9, a1to11); final ArrayList<Alarm> pendingAt0 = mAlarmStore.removePendingAlarms(0); assertEquals(0, pendingAt0.size()); @@ -121,24 +121,24 @@ public class AlarmStoreTest { final ArrayList<Alarm> pendingAt3 = mAlarmStore.removePendingAlarms(3); assertEquals(2, pendingAt3.size()); - assertTrue(pendingAt3.contains(a1_11) && pendingAt3.contains(a2_5)); + assertTrue(pendingAt3.contains(a1to11) && pendingAt3.contains(a2to5)); assertEquals(1, mAlarmStore.size()); - addAlarmsToStore(a2_5, a1_11); + addAlarmsToStore(a2to5, a1to11); final ArrayList<Alarm> pendingAt7 = mAlarmStore.removePendingAlarms(7); assertEquals(3, pendingAt7.size()); - assertTrue(pendingAt7.contains(a1_11) && pendingAt7.contains(a2_5) && pendingAt7.contains( - a6_9)); + assertTrue(pendingAt7.contains(a1to11) && pendingAt7.contains(a2to5) && pendingAt7.contains( + a6to9)); assertEquals(0, mAlarmStore.size()); } @Test public void getNextWakeupDeliveryTime() { - final Alarm a1_10 = createAlarm(1, 9, mock(PendingIntent.class), null); - final Alarm a3_8_wakeup = createWakeupAlarm(3, 5, mock(PendingIntent.class), null); - final Alarm a6_wakeup = createWakeupAlarm(6, 0, mock(PendingIntent.class), null); - final Alarm a5 = createAlarm(5, 0, mock(PendingIntent.class), null); - addAlarmsToStore(a5, a6_wakeup, a3_8_wakeup, a1_10); + final Alarm a1to10 = createAlarm(1, 9, null); + final Alarm a3to8wakeup = createWakeupAlarm(3, 5, null); + final Alarm a6wakeup = createWakeupAlarm(6, 0, null); + final Alarm a5 = createAlarm(5, 0, null); + addAlarmsToStore(a5, a6wakeup, a3to8wakeup, a1to10); // The wakeup alarms are [6] and [3, 8], hence 6 is the latest time till when we can // defer delivering any wakeup alarm. @@ -155,11 +155,11 @@ public class AlarmStoreTest { @Test public void getNextDeliveryTime() { - final Alarm a1_10 = createAlarm(1, 9, mock(PendingIntent.class), null); - final Alarm a3_8_wakeup = createWakeupAlarm(3, 5, mock(PendingIntent.class), null); - final Alarm a6_wakeup = createWakeupAlarm(6, 0, mock(PendingIntent.class), null); - final Alarm a5 = createAlarm(5, 0, mock(PendingIntent.class), null); - addAlarmsToStore(a5, a6_wakeup, a3_8_wakeup, a1_10); + final Alarm a1to10 = createAlarm(1, 9, null); + final Alarm a3to8wakeup = createWakeupAlarm(3, 5, null); + final Alarm a6wakeup = createWakeupAlarm(6, 0, null); + final Alarm a5 = createAlarm(5, 0, null); + addAlarmsToStore(a5, a6wakeup, a3to8wakeup, a1to10); assertTrue(mAlarmStore.getNextDeliveryTime() <= 5); @@ -168,24 +168,22 @@ public class AlarmStoreTest { } @Test - public void recalculateAlarmDeliveries() { - final Alarm a5 = createAlarm(5, 0, mock(PendingIntent.class), null); - final Alarm a8 = createAlarm(8, 0, mock(PendingIntent.class), null); - final Alarm a10 = createAlarm(10, 0, mock(PendingIntent.class), null); + public void updateAlarmDeliveries() { + final Alarm a5 = createAlarm(5, 0, null); + final Alarm a8 = createAlarm(8, 0, null); + final Alarm a10 = createAlarm(10, 0, null); addAlarmsToStore(a8, a10, a5); assertEquals(5, mAlarmStore.getNextDeliveryTime()); - mAlarmStore.recalculateAlarmDeliveries(a -> { - a.whenElapsed += 3; - a.maxWhenElapsed = a.whenElapsed; + mAlarmStore.updateAlarmDeliveries(a -> { + a.setPolicyElapsed(Alarm.REQUESTER_POLICY_INDEX, a.getWhenElapsed() + 3); return true; }); assertEquals(8, mAlarmStore.getNextDeliveryTime()); - mAlarmStore.recalculateAlarmDeliveries(a -> { - a.whenElapsed = 20 - a.whenElapsed; - a.maxWhenElapsed = a.whenElapsed; + mAlarmStore.updateAlarmDeliveries(a -> { + a.setPolicyElapsed(Alarm.REQUESTER_POLICY_INDEX, 20 - a.getWhenElapsed()); return true; }); assertEquals(7, mAlarmStore.getNextDeliveryTime()); diff --git a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmTest.java b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmTest.java new file mode 100644 index 000000000000..efcfae38c3d3 --- /dev/null +++ b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmTest.java @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.alarm; + +import static android.app.AlarmManager.ELAPSED_REALTIME; + +import static com.android.server.alarm.Alarm.APP_STANDBY_POLICY_INDEX; +import static com.android.server.alarm.Alarm.REQUESTER_POLICY_INDEX; +import static com.android.server.alarm.Constants.TEST_CALLING_PACKAGE; +import static com.android.server.alarm.Constants.TEST_CALLING_UID; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.mock; + +import android.app.PendingIntent; +import android.platform.test.annotations.Presubmit; + +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +@Presubmit +@RunWith(AndroidJUnit4.class) +public class AlarmTest { + + private Alarm createDefaultAlarm(long requestedElapsed, long windowLength) { + return new Alarm(ELAPSED_REALTIME, 0, requestedElapsed, windowLength, 0, + mock(PendingIntent.class), null, null, null, 0, null, TEST_CALLING_UID, + TEST_CALLING_PACKAGE); + } + + @Test + public void initSetsOnlyRequesterPolicy() { + final Alarm a = createDefaultAlarm(4567, 2); + assertEquals(4567, a.getPolicyElapsed(REQUESTER_POLICY_INDEX)); + assertEquals(0, a.getPolicyElapsed(APP_STANDBY_POLICY_INDEX)); + } + + @Test + public void whenElapsed() { + final Alarm a = createDefaultAlarm(0, 0); + + a.setPolicyElapsed(REQUESTER_POLICY_INDEX, 4); + a.setPolicyElapsed(APP_STANDBY_POLICY_INDEX, 10); + assertEquals(10, a.getWhenElapsed()); + + a.setPolicyElapsed(REQUESTER_POLICY_INDEX, 12); + assertEquals(12, a.getWhenElapsed()); + + a.setPolicyElapsed(REQUESTER_POLICY_INDEX, 7); + assertEquals(10, a.getWhenElapsed()); + + a.setPolicyElapsed(APP_STANDBY_POLICY_INDEX, 2); + assertEquals(7, a.getWhenElapsed()); + + a.setPolicyElapsed(APP_STANDBY_POLICY_INDEX, 7); + assertEquals(7, a.getWhenElapsed()); + } + + @Test + public void maxWhenElapsed() { + final Alarm a = createDefaultAlarm(10, 12); + assertEquals(22, a.getMaxWhenElapsed()); + + a.setPolicyElapsed(REQUESTER_POLICY_INDEX, 15); + assertEquals(27, a.getMaxWhenElapsed()); + + a.setPolicyElapsed(REQUESTER_POLICY_INDEX, 2); + assertEquals(14, a.getMaxWhenElapsed()); + + a.setPolicyElapsed(APP_STANDBY_POLICY_INDEX, 5); + assertEquals(14, a.getMaxWhenElapsed()); + + a.setPolicyElapsed(APP_STANDBY_POLICY_INDEX, 16); + assertEquals(16, a.getMaxWhenElapsed()); + + a.setPolicyElapsed(APP_STANDBY_POLICY_INDEX, 12); + assertEquals(14, a.getMaxWhenElapsed()); + } + + @Test + public void setPolicyElapsed() { + final Alarm exactAlarm = createDefaultAlarm(10, 0); + + assertTrue(exactAlarm.setPolicyElapsed(REQUESTER_POLICY_INDEX, 4)); + assertTrue(exactAlarm.setPolicyElapsed(APP_STANDBY_POLICY_INDEX, 10)); + + assertFalse(exactAlarm.setPolicyElapsed(REQUESTER_POLICY_INDEX, 8)); + assertFalse(exactAlarm.setPolicyElapsed(REQUESTER_POLICY_INDEX, 10)); + assertFalse(exactAlarm.setPolicyElapsed(APP_STANDBY_POLICY_INDEX, 8)); + + assertTrue(exactAlarm.setPolicyElapsed(REQUESTER_POLICY_INDEX, 7)); + + final Alarm inexactAlarm = createDefaultAlarm(10, 5); + + assertTrue(inexactAlarm.setPolicyElapsed(REQUESTER_POLICY_INDEX, 4)); + assertTrue(inexactAlarm.setPolicyElapsed(APP_STANDBY_POLICY_INDEX, 10)); + + // whenElapsed won't change, but maxWhenElapsed will. + assertTrue(inexactAlarm.setPolicyElapsed(REQUESTER_POLICY_INDEX, 8)); + assertTrue(inexactAlarm.setPolicyElapsed(REQUESTER_POLICY_INDEX, 10)); + + assertFalse(inexactAlarm.setPolicyElapsed(APP_STANDBY_POLICY_INDEX, 8)); + } +} diff --git a/services/tests/mockingservicestests/src/com/android/server/alarm/BackgroundRestrictedAlarmsTest.java b/services/tests/mockingservicestests/src/com/android/server/alarm/BackgroundRestrictedAlarmsTest.java index 6465739f6822..5bb6a42b2604 100644 --- a/services/tests/mockingservicestests/src/com/android/server/alarm/BackgroundRestrictedAlarmsTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/alarm/BackgroundRestrictedAlarmsTest.java @@ -45,7 +45,7 @@ public class BackgroundRestrictedAlarmsTest { } uidAlarms.add(new Alarm( removeIt ? RTC : RTC_WAKEUP, - 0, 0, 0, 0, 0, null, null, null, null, 0, null, uid, name)); + 0, 0, 0, 0, null, null, null, null, 0, null, uid, name)); return all; } diff --git a/services/tests/mockingservicestests/src/com/android/server/alarm/Constants.java b/services/tests/mockingservicestests/src/com/android/server/alarm/Constants.java new file mode 100644 index 000000000000..2552db8a310a --- /dev/null +++ b/services/tests/mockingservicestests/src/com/android/server/alarm/Constants.java @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.alarm; + +public interface Constants { + String TEST_CALLING_PACKAGE = "com.android.framework.test-package"; + int TEST_CALLING_UID = 67890; +} diff --git a/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java b/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java index 9d2393b4a8f7..0ab15017a2b4 100644 --- a/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java @@ -83,6 +83,8 @@ public class LocalDisplayAdapterTest { private LinkedList<DisplayAddress.Physical> mAddresses = new LinkedList<>(); + private Injector mInjector; + @Before public void setUp() throws Exception { mMockitoSession = mockitoSession() @@ -94,8 +96,9 @@ public class LocalDisplayAdapterTest { doReturn(mMockedResources).when(mMockedContext).getResources(); LocalServices.removeServiceForTest(LightsManager.class); LocalServices.addService(LightsManager.class, mMockedLightsManager); + mInjector = new Injector(); mAdapter = new LocalDisplayAdapter(mMockedSyncRoot, mMockedContext, mHandler, - mListener); + mListener, mInjector); spyOn(mAdapter); doReturn(mMockedContext).when(mAdapter).getOverlayContext(); } @@ -222,7 +225,7 @@ public class LocalDisplayAdapterTest { display.configs = configs; display.activeConfig = 1; setUpDisplay(display); - mAdapter.registerLocked(); + mInjector.getTransmitter().sendHotplug(display, /* connected */ true); waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS); assertThat(SurfaceControl.getActiveConfig(display.token)).isEqualTo(1); @@ -272,7 +275,7 @@ public class LocalDisplayAdapterTest { new int[Display.HdrCapabilities.HDR_TYPE_HDR10_PLUS], 1000, 1000, 0); display.hdrCapabilities = changedHdrCapabilities; setUpDisplay(display); - mAdapter.registerLocked(); + mInjector.getTransmitter().sendHotplug(display, /* connected */ true); waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS); assertThat(mListener.addedDisplays.size()).isEqualTo(1); @@ -308,7 +311,7 @@ public class LocalDisplayAdapterTest { final int[] changedColorModes = new int[]{Display.COLOR_MODE_DEFAULT}; display.colorModes = changedColorModes; setUpDisplay(display); - mAdapter.registerLocked(); + mInjector.getTransmitter().sendHotplug(display, /* connected */ true); waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS); assertThat(mListener.addedDisplays.size()).isEqualTo(1); @@ -322,6 +325,35 @@ public class LocalDisplayAdapterTest { assertThat(displayDeviceInfo.supportedColorModes).isEqualTo(changedColorModes); } + @Test + public void testDisplayChange_withStaleDesiredDisplayConfigSpecs() throws Exception { + SurfaceControl.DisplayConfig[] configs = new SurfaceControl.DisplayConfig[]{ + createFakeDisplayConfig(1920, 1080, 60f), + createFakeDisplayConfig(1920, 1080, 50f) + }; + final int activeConfig = 0; + FakeDisplay display = new FakeDisplay(PORT_A, configs, activeConfig); + display.desiredDisplayConfigSpecs.defaultConfig = 1; + + setUpDisplay(display); + updateAvailableDisplays(); + mAdapter.registerLocked(); + waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS); + + // Change the display + display.configs = new SurfaceControl.DisplayConfig[]{ + createFakeDisplayConfig(1920, 1080, 60f) + }; + // SurfaceFlinger can return a stale defaultConfig. Make sure this doesn't + // trigger ArrayOutOfBoundsException. + display.desiredDisplayConfigSpecs.defaultConfig = 1; + + setUpDisplay(display); + updateAvailableDisplays(); + mInjector.getTransmitter().sendHotplug(display, /* connected */ true); + waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS); + } + private void assertDisplayDpi(DisplayDeviceInfo info, int expectedPort, float expectedXdpi, float expectedYDpi, @@ -356,6 +388,8 @@ public class LocalDisplayAdapterTest { public int[] colorModes = new int[]{ Display.COLOR_MODE_DEFAULT }; public Display.HdrCapabilities hdrCapabilities = new Display.HdrCapabilities(new int[0], 1000, 1000, 0); + public SurfaceControl.DesiredDisplayConfigSpecs desiredDisplayConfigSpecs = + new SurfaceControl.DesiredDisplayConfigSpecs(0, 60.f, 60.f, 60.f, 60.f); private FakeDisplay(int port) { this.address = createDisplayAddress(port); @@ -387,7 +421,7 @@ public class LocalDisplayAdapterTest { () -> SurfaceControl.getDisplayColorModes(display.token)); doReturn(display.hdrCapabilities).when( () -> SurfaceControl.getHdrCapabilities(display.token)); - doReturn(new SurfaceControl.DesiredDisplayConfigSpecs(0, 60.f, 60.f, 60.f, 60.f)) + doReturn(display.desiredDisplayConfigSpecs) .when(() -> SurfaceControl.getDesiredDisplayConfigSpecs(display.token)); } @@ -422,7 +456,7 @@ public class LocalDisplayAdapterTest { return config; } - private void waitForHandlerToComplete(Handler handler, long waitTimeMs) + private static void waitForHandlerToComplete(Handler handler, long waitTimeMs) throws InterruptedException { final Object lock = new Object(); synchronized (lock) { @@ -435,6 +469,35 @@ public class LocalDisplayAdapterTest { } } + private class HotplugTransmitter { + private final Handler mHandler; + private final LocalDisplayAdapter.DisplayEventListener mListener; + + HotplugTransmitter(Looper looper, LocalDisplayAdapter.DisplayEventListener listener) { + mHandler = new Handler(looper); + mListener = listener; + } + + public void sendHotplug(FakeDisplay display, boolean connected) + throws InterruptedException { + mHandler.post(() -> mListener.onHotplug(/* timestampNanos = */ 0, + display.address.getPhysicalDisplayId(), connected)); + waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS); + } + } + + private class Injector extends LocalDisplayAdapter.Injector { + private HotplugTransmitter mTransmitter; + @Override + public void setDisplayEventListenerLocked(Looper looper, + LocalDisplayAdapter.DisplayEventListener listener) { + mTransmitter = new HotplugTransmitter(looper, listener); + } + public HotplugTransmitter getTransmitter() { + return mTransmitter; + } + } + private class TestListener implements DisplayAdapter.Listener { public ArrayList<DisplayDevice> addedDisplays = new ArrayList<>(); public ArrayList<DisplayDevice> changedDisplays = new ArrayList<>(); diff --git a/services/tests/mockingservicestests/src/com/android/server/location/LocationProviderManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/location/LocationProviderManagerTest.java index 31ec4a53908c..3aedd3c7d753 100644 --- a/services/tests/mockingservicestests/src/com/android/server/location/LocationProviderManagerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/location/LocationProviderManagerTest.java @@ -710,7 +710,6 @@ public class LocationProviderManagerTest { @Test public void testProviderRequest() { assertThat(mProvider.getRequest().isActive()).isFalse(); - assertThat(mProvider.getRequest().getLocationRequests()).isEmpty(); ILocationListener listener1 = createMockLocationListener(); LocationRequest request1 = new LocationRequest.Builder(5).setWorkSource( @@ -718,7 +717,6 @@ public class LocationProviderManagerTest { mManager.registerLocationRequest(request1, IDENTITY, PERMISSION_FINE, listener1); assertThat(mProvider.getRequest().isActive()).isTrue(); - assertThat(mProvider.getRequest().getLocationRequests()).containsExactly(request1); assertThat(mProvider.getRequest().isLocationSettingsIgnored()).isFalse(); assertThat(mProvider.getRequest().getIntervalMillis()).isEqualTo(5); assertThat(mProvider.getRequest().isLowPower()).isFalse(); @@ -732,8 +730,6 @@ public class LocationProviderManagerTest { mManager.registerLocationRequest(request2, IDENTITY, PERMISSION_FINE, listener2); assertThat(mProvider.getRequest().isActive()).isTrue(); - assertThat(mProvider.getRequest().getLocationRequests()).containsExactly(request1, - request2); assertThat(mProvider.getRequest().isLocationSettingsIgnored()).isFalse(); assertThat(mProvider.getRequest().getIntervalMillis()).isEqualTo(1); assertThat(mProvider.getRequest().isLowPower()).isFalse(); @@ -742,7 +738,6 @@ public class LocationProviderManagerTest { mManager.unregisterLocationRequest(listener1); assertThat(mProvider.getRequest().isActive()).isTrue(); - assertThat(mProvider.getRequest().getLocationRequests()).containsExactly(request2); assertThat(mProvider.getRequest().isLocationSettingsIgnored()).isFalse(); assertThat(mProvider.getRequest().getIntervalMillis()).isEqualTo(1); assertThat(mProvider.getRequest().isLowPower()).isTrue(); @@ -751,7 +746,6 @@ public class LocationProviderManagerTest { mManager.unregisterLocationRequest(listener2); assertThat(mProvider.getRequest().isActive()).isFalse(); - assertThat(mProvider.getRequest().getLocationRequests()).isEmpty(); } @Test @@ -855,7 +849,6 @@ public class LocationProviderManagerTest { mInjector.getSettingsHelper().setLocationEnabled(false, IDENTITY.getUserId()); assertThat(mProvider.getRequest().isActive()).isTrue(); - assertThat(mProvider.getRequest().getLocationRequests()).containsExactly(request2); assertThat(mProvider.getRequest().getIntervalMillis()).isEqualTo(5); assertThat(mProvider.getRequest().isLocationSettingsIgnored()).isTrue(); } diff --git a/services/tests/servicestests/src/com/android/server/BluetoothAirplaneModeListenerTest.java b/services/tests/servicestests/src/com/android/server/BluetoothAirplaneModeListenerTest.java index 968a402ff3b7..3ace3f4c79dc 100644 --- a/services/tests/servicestests/src/com/android/server/BluetoothAirplaneModeListenerTest.java +++ b/services/tests/servicestests/src/com/android/server/BluetoothAirplaneModeListenerTest.java @@ -27,8 +27,6 @@ import androidx.test.InstrumentationRegistry; import androidx.test.filters.MediumTest; import androidx.test.runner.AndroidJUnit4; -import com.android.server.BluetoothAirplaneModeListener.AirplaneModeHelper; - import org.junit.Assert; import org.junit.Before; import org.junit.Test; @@ -41,7 +39,7 @@ public class BluetoothAirplaneModeListenerTest { private Context mContext; private BluetoothAirplaneModeListener mBluetoothAirplaneModeListener; private BluetoothAdapter mBluetoothAdapter; - private AirplaneModeHelper mHelper; + private BluetoothModeChangeHelper mHelper; @Mock BluetoothManagerService mBluetoothManagerService; @@ -49,7 +47,7 @@ public class BluetoothAirplaneModeListenerTest { public void setUp() throws Exception { mContext = InstrumentationRegistry.getTargetContext(); - mHelper = mock(AirplaneModeHelper.class); + mHelper = mock(BluetoothModeChangeHelper.class); when(mHelper.getSettingsInt(BluetoothAirplaneModeListener.TOAST_COUNT)) .thenReturn(BluetoothAirplaneModeListener.MAX_TOAST_COUNT); doNothing().when(mHelper).setSettingsInt(anyString(), anyInt()); diff --git a/services/tests/servicestests/src/com/android/server/GestureLauncherServiceTest.java b/services/tests/servicestests/src/com/android/server/GestureLauncherServiceTest.java index c91bb93fc559..d2d85c860cd5 100644 --- a/services/tests/servicestests/src/com/android/server/GestureLauncherServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/GestureLauncherServiceTest.java @@ -223,6 +223,25 @@ public class GestureLauncherServiceTest { } @Test + public void testInterceptPowerKeyDown_firstPowerDown_panicGestureNotLaunched() { + withPanicGestureEnabledSettingValue(true); + mGestureLauncherService.updatePanicButtonGestureEnabled(); + + long eventTime = INITIAL_EVENT_TIME_MILLIS + + GestureLauncherService.POWER_SHORT_TAP_SEQUENCE_MAX_INTERVAL_MS - 1; + KeyEvent keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE, + IGNORED_REPEAT); + boolean interactive = true; + MutableBoolean outLaunched = new MutableBoolean(true); + boolean intercepted = mGestureLauncherService.interceptPowerKeyDown(keyEvent, interactive, + outLaunched); + + assertFalse(intercepted); + assertFalse(outLaunched.value); + verify(mMetricsLogger).histogram("power_double_tap_interval", (int) eventTime); + } + + @Test public void testInterceptPowerKeyDown_intervalInBoundsCameraPowerGestureOffInteractive() { withCameraDoubleTapPowerEnableConfigValue(false); withCameraDoubleTapPowerDisableSettingValue(1); @@ -405,6 +424,200 @@ public class GestureLauncherServiceTest { } @Test + public void + testInterceptPowerKeyDown_fiveInboundPresses_cameraAndPanicEnabled_bothLaunch() { + withCameraDoubleTapPowerEnableConfigValue(true); + withCameraDoubleTapPowerDisableSettingValue(0); + withPanicGestureEnabledSettingValue(true); + mGestureLauncherService.updateCameraDoubleTapPowerEnabled(); + mGestureLauncherService.updatePanicButtonGestureEnabled(); + withUserSetupCompleteValue(true); + + // First button press does nothing + long eventTime = INITIAL_EVENT_TIME_MILLIS; + KeyEvent keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE, + IGNORED_REPEAT); + boolean interactive = true; + MutableBoolean outLaunched = new MutableBoolean(true); + boolean intercepted = mGestureLauncherService.interceptPowerKeyDown(keyEvent, interactive, + outLaunched); + assertFalse(intercepted); + assertFalse(outLaunched.value); + + final long interval = GestureLauncherService.CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1; + + // 2nd button triggers camera + eventTime += interval; + keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE, + IGNORED_REPEAT); + outLaunched.value = false; + intercepted = mGestureLauncherService.interceptPowerKeyDown(keyEvent, interactive, + outLaunched); + assertTrue(intercepted); + assertTrue(outLaunched.value); + + // Camera checks + verify(mStatusBarManagerInternal).onCameraLaunchGestureDetected( + StatusBarManager.CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP); + verify(mMetricsLogger) + .action(MetricsEvent.ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE, (int) interval); + + final ArgumentCaptor<Integer> cameraIntervalCaptor = ArgumentCaptor.forClass(Integer.class); + verify(mMetricsLogger, times(2)).histogram( + eq("power_double_tap_interval"), cameraIntervalCaptor.capture()); + List<Integer> cameraIntervals = cameraIntervalCaptor.getAllValues(); + assertEquals((int) INITIAL_EVENT_TIME_MILLIS, cameraIntervals.get(0).intValue()); + assertEquals((int) interval, cameraIntervals.get(1).intValue()); + + final ArgumentCaptor<Integer> tapCountCaptor = ArgumentCaptor.forClass(Integer.class); + verify(mMetricsLogger, times(2)).histogram( + eq("power_consecutive_short_tap_count"), tapCountCaptor.capture()); + List<Integer> tapCounts = tapCountCaptor.getAllValues(); + assertEquals(1, tapCounts.get(0).intValue()); + assertEquals(2, tapCounts.get(1).intValue()); + + // Continue the button presses for the panic gesture. + + // Presses 3 and 4 should not trigger any gesture + for (int i = 0; i < 2; i++) { + eventTime += interval; + keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE, + IGNORED_REPEAT); + outLaunched.value = false; + intercepted = mGestureLauncherService.interceptPowerKeyDown(keyEvent, interactive, + outLaunched); + assertTrue(intercepted); + assertFalse(outLaunched.value); + } + + // Fifth button press should trigger the panic flow + eventTime += interval; + keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE, + IGNORED_REPEAT); + outLaunched.value = false; + intercepted = mGestureLauncherService.interceptPowerKeyDown(keyEvent, interactive, + outLaunched); + assertTrue(intercepted); + assertTrue(outLaunched.value); + + // TODO (b/169960245) Verify metric event equiv. to ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE + verify(mStatusBarManagerInternal).onEmergencyActionLaunchGestureDetected(); + + final ArgumentCaptor<Integer> intervalCaptor = ArgumentCaptor.forClass(Integer.class); + verify(mMetricsLogger, times(5)).histogram( + eq("power_double_tap_interval"), intervalCaptor.capture()); + List<Integer> intervals = intervalCaptor.getAllValues(); + assertEquals((int) INITIAL_EVENT_TIME_MILLIS, intervals.get(0).intValue()); + assertEquals((int) interval, intervals.get(1).intValue()); + } + + @Test + public void + testInterceptPowerKeyDown_fiveInboundPresses_panicGestureEnabled_launchesPanicFlow() { + withPanicGestureEnabledSettingValue(true); + mGestureLauncherService.updatePanicButtonGestureEnabled(); + withUserSetupCompleteValue(true); + + // First button press does nothing + long eventTime = INITIAL_EVENT_TIME_MILLIS; + KeyEvent keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE, + IGNORED_REPEAT); + boolean interactive = true; + MutableBoolean outLaunched = new MutableBoolean(true); + boolean intercepted = mGestureLauncherService.interceptPowerKeyDown(keyEvent, interactive, + outLaunched); + assertFalse(intercepted); + assertFalse(outLaunched.value); + + final long interval = GestureLauncherService.CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1; + // 3 more button presses which should not trigger any gesture (camera gesture disabled) + for (int i = 0; i < 3; i++) { + eventTime += interval; + keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE, + IGNORED_REPEAT); + outLaunched.value = false; + intercepted = mGestureLauncherService.interceptPowerKeyDown(keyEvent, interactive, + outLaunched); + assertTrue(intercepted); + assertFalse(outLaunched.value); + } + + // Fifth button press should trigger the panic flow + eventTime += interval; + keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE, + IGNORED_REPEAT); + outLaunched.value = false; + intercepted = mGestureLauncherService.interceptPowerKeyDown(keyEvent, interactive, + outLaunched); + assertTrue(outLaunched.value); + assertTrue(intercepted); + + // TODO (b/169960245) Verify metric event equiv. to ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE + verify(mStatusBarManagerInternal).onEmergencyActionLaunchGestureDetected(); + + final ArgumentCaptor<Integer> intervalCaptor = ArgumentCaptor.forClass(Integer.class); + verify(mMetricsLogger, times(5)).histogram( + eq("power_double_tap_interval"), intervalCaptor.capture()); + List<Integer> intervals = intervalCaptor.getAllValues(); + assertEquals((int) INITIAL_EVENT_TIME_MILLIS, intervals.get(0).intValue()); + assertEquals((int) interval, intervals.get(1).intValue()); + } + + @Test + public void + testInterceptPowerKeyDown_tenInboundPresses_panicGestureEnabled_pressesIntercepted() { + withPanicGestureEnabledSettingValue(true); + mGestureLauncherService.updatePanicButtonGestureEnabled(); + withUserSetupCompleteValue(true); + + // First button press does nothing + long eventTime = INITIAL_EVENT_TIME_MILLIS; + KeyEvent keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE, + IGNORED_REPEAT); + boolean interactive = true; + MutableBoolean outLaunched = new MutableBoolean(true); + boolean intercepted = mGestureLauncherService.interceptPowerKeyDown(keyEvent, interactive, + outLaunched); + assertFalse(intercepted); + assertFalse(outLaunched.value); + + final long interval = GestureLauncherService.CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1; + // 3 more button presses which should not trigger any gesture, but intercepts action. + for (int i = 0; i < 3; i++) { + eventTime += interval; + keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE, + IGNORED_REPEAT); + outLaunched.value = false; + intercepted = mGestureLauncherService.interceptPowerKeyDown(keyEvent, interactive, + outLaunched); + assertTrue(intercepted); + assertFalse(outLaunched.value); + } + + // Fifth button press should trigger the panic flow + eventTime += interval; + keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE, + IGNORED_REPEAT); + outLaunched.value = false; + intercepted = mGestureLauncherService.interceptPowerKeyDown(keyEvent, interactive, + outLaunched); + assertTrue(outLaunched.value); + assertTrue(intercepted); + + // 5 more button presses which should not trigger any gesture, but intercepts action. + for (int i = 0; i < 5; i++) { + eventTime += interval; + keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE, + IGNORED_REPEAT); + outLaunched.value = false; + intercepted = mGestureLauncherService.interceptPowerKeyDown(keyEvent, interactive, + outLaunched); + assertTrue(intercepted); + assertFalse(outLaunched.value); + } + } + + @Test public void testInterceptPowerKeyDown_longpress() { withCameraDoubleTapPowerEnableConfigValue(true); withCameraDoubleTapPowerDisableSettingValue(0); diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java index 03dce4c62fe8..241b5a9523b1 100644 --- a/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java @@ -76,6 +76,7 @@ import androidx.test.filters.SmallTest; import com.android.server.LocalServices; import com.android.server.am.ProcessList.IsolatedUidRange; import com.android.server.am.ProcessList.IsolatedUidRangeAllocator; +import com.android.server.am.UidObserverController.ChangeRecord; import com.android.server.appop.AppOpsService; import com.android.server.wm.ActivityTaskManagerService; @@ -539,15 +540,15 @@ public class ActivityManagerServiceTest { ActivityManager.PROCESS_STATE_CACHED_ACTIVITY, ActivityManager.PROCESS_STATE_TOP }; - final Map<Integer, UidRecord.ChangeItem> changeItems = new HashMap<>(); + final Map<Integer, ChangeRecord> changeItems = new HashMap<>(); for (int i = 0; i < changesForPendingUidRecords.length; ++i) { - final UidRecord.ChangeItem pendingChange = new UidRecord.ChangeItem(); + final ChangeRecord pendingChange = new ChangeRecord(); pendingChange.change = changesForPendingUidRecords[i]; pendingChange.uid = i; - pendingChange.processState = procStatesForPendingUidRecords[i]; + pendingChange.procState = procStatesForPendingUidRecords[i]; pendingChange.procStateSeq = i; changeItems.put(changesForPendingUidRecords[i], pendingChange); - mAms.mUidObserverController.mPendingUidChanges.add(pendingChange); + addPendingUidChange(pendingChange); } mAms.mUidObserverController.dispatchUidsChanged(); @@ -602,7 +603,7 @@ public class ActivityManagerServiceTest { verifyObserverReceivedChanges(observerToTest, changesToVerify, changeItems, (observer, changeItem) -> { verify(observer).onUidStateChanged(changeItem.uid, - changeItem.processState, changeItem.procStateSeq, + changeItem.procState, changeItem.procStateSeq, ActivityManager.PROCESS_CAPABILITY_NONE); }); } @@ -612,14 +613,14 @@ public class ActivityManagerServiceTest { } private interface ObserverChangesVerifier { - void verify(IUidObserver observer, UidRecord.ChangeItem changeItem) throws RemoteException; + void verify(IUidObserver observer, ChangeRecord changeItem) throws RemoteException; } private void verifyObserverReceivedChanges(IUidObserver observer, int[] changesToVerify, - Map<Integer, UidRecord.ChangeItem> changeItems, ObserverChangesVerifier verifier) + Map<Integer, ChangeRecord> changeItems, ObserverChangesVerifier verifier) throws RemoteException { for (int change : changesToVerify) { - final UidRecord.ChangeItem changeItem = changeItems.get(change); + final ChangeRecord changeItem = changeItems.get(change); verifier.verify(observer, changeItem); } } @@ -641,59 +642,59 @@ public class ActivityManagerServiceTest { // So, resetting the mock here. Mockito.reset(observer); - final UidRecord.ChangeItem changeItem = new UidRecord.ChangeItem(); + final ChangeRecord changeItem = new ChangeRecord(); changeItem.uid = TEST_UID; changeItem.change = UidRecord.CHANGE_PROCSTATE; - changeItem.processState = ActivityManager.PROCESS_STATE_LAST_ACTIVITY; + changeItem.procState = ActivityManager.PROCESS_STATE_LAST_ACTIVITY; changeItem.procStateSeq = 111; - mAms.mUidObserverController.mPendingUidChanges.add(changeItem); + addPendingUidChange(changeItem); mAms.mUidObserverController.dispatchUidsChanged(); // First process state message is always delivered regardless of whether the process state // change is above or below the cutpoint (PROCESS_STATE_SERVICE). verify(observer).onUidStateChanged(TEST_UID, - changeItem.processState, changeItem.procStateSeq, + changeItem.procState, changeItem.procStateSeq, ActivityManager.PROCESS_CAPABILITY_NONE); verifyNoMoreInteractions(observer); - changeItem.processState = ActivityManager.PROCESS_STATE_RECEIVER; - mAms.mUidObserverController.mPendingUidChanges.add(changeItem); + changeItem.procState = ActivityManager.PROCESS_STATE_RECEIVER; + addPendingUidChange(changeItem); mAms.mUidObserverController.dispatchUidsChanged(); // Previous process state change is below cutpoint (PROCESS_STATE_SERVICE) and // the current process state change is also below cutpoint, so no callback will be invoked. verifyNoMoreInteractions(observer); - changeItem.processState = ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE; - mAms.mUidObserverController.mPendingUidChanges.add(changeItem); + changeItem.procState = ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE; + addPendingUidChange(changeItem); mAms.mUidObserverController.dispatchUidsChanged(); // Previous process state change is below cutpoint (PROCESS_STATE_SERVICE) and // the current process state change is above cutpoint, so callback will be invoked with the // current process state change. verify(observer).onUidStateChanged(TEST_UID, - changeItem.processState, changeItem.procStateSeq, + changeItem.procState, changeItem.procStateSeq, ActivityManager.PROCESS_CAPABILITY_NONE); verifyNoMoreInteractions(observer); - changeItem.processState = ActivityManager.PROCESS_STATE_TOP; - mAms.mUidObserverController.mPendingUidChanges.add(changeItem); + changeItem.procState = ActivityManager.PROCESS_STATE_TOP; + addPendingUidChange(changeItem); mAms.mUidObserverController.dispatchUidsChanged(); // Previous process state change is above cutpoint (PROCESS_STATE_SERVICE) and // the current process state change is also above cutpoint, so no callback will be invoked. verifyNoMoreInteractions(observer); - changeItem.processState = ActivityManager.PROCESS_STATE_CACHED_EMPTY; - mAms.mUidObserverController.mPendingUidChanges.add(changeItem); + changeItem.procState = ActivityManager.PROCESS_STATE_CACHED_EMPTY; + addPendingUidChange(changeItem); mAms.mUidObserverController.dispatchUidsChanged(); // Previous process state change is above cutpoint (PROCESS_STATE_SERVICE) and // the current process state change is below cutpoint, so callback will be invoked with the // current process state change. verify(observer).onUidStateChanged(TEST_UID, - changeItem.processState, changeItem.procStateSeq, + changeItem.procState, changeItem.procStateSeq, ActivityManager.PROCESS_CAPABILITY_NONE); verifyNoMoreInteractions(observer); } /** - * This test verifies that {@link ActivityManagerService#mValidateUids} which is a + * This test verifies that {@link UidObserverController#getValidateUidsForTest()} which is a * part of dumpsys is correctly updated. */ @Test @@ -707,45 +708,45 @@ public class ActivityManagerServiceTest { ActivityManager.PROCESS_STATE_SERVICE, ActivityManager.PROCESS_STATE_RECEIVER }; - final ArrayList<UidRecord.ChangeItem> pendingItemsForUids = + final ArrayList<ChangeRecord> pendingItemsForUids = new ArrayList<>(changesForPendingItems.length); for (int i = 0; i < changesForPendingItems.length; ++i) { - final UidRecord.ChangeItem item = new UidRecord.ChangeItem(); + final ChangeRecord item = new ChangeRecord(); item.uid = i; item.change = changesForPendingItems[i]; - item.processState = procStatesForPendingItems[i]; + item.procState = procStatesForPendingItems[i]; pendingItemsForUids.add(i, item); } // Verify that when there no observers listening to uid state changes, then there will // be no changes to validateUids. - mAms.mUidObserverController.mPendingUidChanges.addAll(pendingItemsForUids); + addPendingUidChanges(pendingItemsForUids); mAms.mUidObserverController.dispatchUidsChanged(); assertEquals("No observers registered, so validateUids should be empty", - 0, mAms.mUidObserverController.mValidateUids.size()); + 0, mAms.mUidObserverController.getValidateUidsForTest().size()); final IUidObserver observer = mock(IUidObserver.Stub.class); when(observer.asBinder()).thenReturn((IBinder) observer); mAms.registerUidObserver(observer, 0, 0, null); // Verify that when observers are registered, then validateUids is correctly updated. - mAms.mUidObserverController.mPendingUidChanges.addAll(pendingItemsForUids); + addPendingUidChanges(pendingItemsForUids); mAms.mUidObserverController.dispatchUidsChanged(); for (int i = 0; i < pendingItemsForUids.size(); ++i) { - final UidRecord.ChangeItem item = pendingItemsForUids.get(i); + final ChangeRecord item = pendingItemsForUids.get(i); final UidRecord validateUidRecord = - mAms.mUidObserverController.mValidateUids.get(item.uid); + mAms.mUidObserverController.getValidateUidsForTest().get(item.uid); if ((item.change & UidRecord.CHANGE_GONE) != 0) { assertNull("validateUidRecord should be null since the change is either " + "CHANGE_GONE or CHANGE_GONE_IDLE", validateUidRecord); } else { assertNotNull("validateUidRecord should not be null since the change is neither " + "CHANGE_GONE nor CHANGE_GONE_IDLE", validateUidRecord); - assertEquals("processState: " + item.processState + " curProcState: " + assertEquals("processState: " + item.procState + " curProcState: " + validateUidRecord.getCurProcState() + " should have been equal", - item.processState, validateUidRecord.getCurProcState()); - assertEquals("processState: " + item.processState + " setProcState: " + item.procState, validateUidRecord.getCurProcState()); + assertEquals("processState: " + item.procState + " setProcState: " + validateUidRecord.getCurProcState() + " should have been equal", - item.processState, validateUidRecord.setProcState); + item.procState, validateUidRecord.setProcState); if (item.change == UidRecord.CHANGE_IDLE) { assertTrue("UidRecord.idle should be updated to true for CHANGE_IDLE", validateUidRecord.idle); @@ -759,19 +760,19 @@ public class ActivityManagerServiceTest { // Verify that when uid state changes to CHANGE_GONE or CHANGE_GONE_IDLE, then it // will be removed from validateUids. assertNotEquals("validateUids should not be empty", 0, - mAms.mUidObserverController.mValidateUids.size()); + mAms.mUidObserverController.getValidateUidsForTest().size()); for (int i = 0; i < pendingItemsForUids.size(); ++i) { - final UidRecord.ChangeItem item = pendingItemsForUids.get(i); + final ChangeRecord item = pendingItemsForUids.get(i); // Assign CHANGE_GONE_IDLE to some items and CHANGE_GONE to the others, using even/odd // distribution for this assignment. item.change = (i % 2) == 0 ? (UidRecord.CHANGE_GONE | UidRecord.CHANGE_IDLE) : UidRecord.CHANGE_GONE; } - mAms.mUidObserverController.mPendingUidChanges.addAll(pendingItemsForUids); + addPendingUidChanges(pendingItemsForUids); mAms.mUidObserverController.dispatchUidsChanged(); assertEquals("validateUids should be empty, size=" - + mAms.mUidObserverController.mValidateUids.size(), - 0, mAms.mUidObserverController.mValidateUids.size()); + + mAms.mUidObserverController.getValidateUidsForTest().size(), + 0, mAms.mUidObserverController.getValidateUidsForTest().size()); } @Test @@ -784,8 +785,6 @@ public class ActivityManagerServiceTest { // Add a pending change for TEST_UID and verify enqueueUidChangeLocked still works as // expected. - final UidRecord.ChangeItem changeItem = new UidRecord.ChangeItem(); - uidRecord.pendingChange = changeItem; uidRecord.curProcStateSeq = TEST_PROC_STATE_SEQ2; verifyLastProcStateSeqUpdated(uidRecord, -1, TEST_PROC_STATE_SEQ2); } @@ -793,7 +792,7 @@ public class ActivityManagerServiceTest { @Test public void testEnqueueUidChangeLocked_nullUidRecord() { // Use "null" uidRecord to make sure there is no crash. - mAms.mUidObserverController.enqueueUidChangeLocked(null, TEST_UID, UidRecord.CHANGE_ACTIVE); + mAms.enqueueUidChangeLocked(null, TEST_UID, UidRecord.CHANGE_ACTIVE); } private void verifyLastProcStateSeqUpdated(UidRecord uidRecord, int uid, long curProcstateSeq) { @@ -802,7 +801,7 @@ public class ActivityManagerServiceTest { final int changeToDispatch = UID_RECORD_CHANGES[i]; // Reset lastProcStateSeqDispatchToObservers after every test. uidRecord.lastDispatchedProcStateSeq = 0; - mAms.mUidObserverController.enqueueUidChangeLocked(uidRecord, uid, changeToDispatch); + mAms.enqueueUidChangeLocked(uidRecord, uid, changeToDispatch); // Verify there is no effect on curProcStateSeq. assertEquals(curProcstateSeq, uidRecord.curProcStateSeq); if ((changeToDispatch & UidRecord.CHANGE_GONE) != 0) { @@ -833,16 +832,17 @@ public class ActivityManagerServiceTest { // Reset the current state mHandler.reset(); - uidRecord.pendingChange = null; - mAms.mUidObserverController.mPendingUidChanges.clear(); + clearPendingUidChanges(); + uidRecord.pendingChange.isPending = false; - mAms.mUidObserverController.enqueueUidChangeLocked(uidRecord, -1, changeToDispatch); + mAms.enqueueUidChangeLocked(uidRecord, -1, changeToDispatch); - // Verify that UidRecord.pendingChange is updated correctly. - assertNotNull(uidRecord.pendingChange); - assertEquals(TEST_UID, uidRecord.pendingChange.uid); - assertEquals(expectedProcState, uidRecord.pendingChange.processState); - assertEquals(TEST_PROC_STATE_SEQ1, uidRecord.pendingChange.procStateSeq); + // Verify that pendingChange is updated correctly. + final ChangeRecord pendingChange = uidRecord.pendingChange; + assertTrue(pendingChange.isPending); + assertEquals(TEST_UID, pendingChange.uid); + assertEquals(expectedProcState, pendingChange.procState); + assertEquals(TEST_PROC_STATE_SEQ1, pendingChange.procStateSeq); // TODO: Verify that DISPATCH_UIDS_CHANGED_UI_MSG is posted to handler. } @@ -923,6 +923,23 @@ public class ActivityManagerServiceTest { mAms.mProcessList.mActiveUids.clear(); } + private void addPendingUidChange(ChangeRecord record) { + mAms.mUidObserverController.getPendingUidChangesForTest().add(record); + } + + private void addPendingUidChanges(ArrayList<ChangeRecord> changes) { + final ArrayList<ChangeRecord> pendingChanges = + mAms.mUidObserverController.getPendingUidChangesForTest(); + for (int i = 0; i < changes.size(); ++i) { + final ChangeRecord record = changes.get(i); + pendingChanges.add(record); + } + } + + private void clearPendingUidChanges() { + mAms.mUidObserverController.getPendingUidChangesForTest().clear(); + } + private static class TestHandler extends Handler { private static final long WAIT_FOR_MSG_TIMEOUT_MS = 4000; // 4 sec private static final long WAIT_FOR_MSG_INTERVAL_MS = 400; // 0.4 sec diff --git a/services/tests/servicestests/src/com/android/server/am/UidObserverControllerTest.java b/services/tests/servicestests/src/com/android/server/am/UidObserverControllerTest.java new file mode 100644 index 000000000000..1de5f6f005d1 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/am/UidObserverControllerTest.java @@ -0,0 +1,253 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.am; + +import static android.app.ActivityManager.PROCESS_CAPABILITY_ALL; +import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_LOCATION; +import static android.app.ActivityManager.PROCESS_CAPABILITY_NONE; +import static android.app.ActivityManager.PROCESS_STATE_CACHED_RECENT; +import static android.app.ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE; +import static android.app.ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND; +import static android.app.ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND; +import static android.app.ActivityManager.PROCESS_STATE_RECEIVER; +import static android.app.ActivityManager.PROCESS_STATE_SERVICE; +import static android.app.ActivityManager.PROCESS_STATE_TOP; + +import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertNotNull; +import static junit.framework.Assert.assertNull; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.when; + +import android.app.ActivityManager; +import android.app.IUidObserver; +import android.os.Handler; +import android.os.IBinder; +import android.os.RemoteException; +import android.util.DebugUtils; +import android.util.Pair; +import android.util.SparseArray; + +import androidx.test.filters.SmallTest; + +import com.android.server.am.UidObserverController.ChangeRecord; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; + +import java.util.ArrayList; + +@SmallTest +public class UidObserverControllerTest { + private static final int TEST_UID1 = 1111; + private static final int TEST_UID2 = 2222; + private static final int TEST_UID3 = 3333; + + private static final String TEST_PKG1 = "com.example1"; + private static final String TEST_PKG2 = "com.example2"; + private static final String TEST_PKG3 = "com.example3"; + + private UidObserverController mUidObserverController; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mUidObserverController = new UidObserverController(mock(Handler.class)); + } + + @Test + public void testEnqueueUidChange() { + int change = mUidObserverController.enqueueUidChange(null, TEST_UID1, + UidRecord.CHANGE_ACTIVE, PROCESS_STATE_FOREGROUND_SERVICE, + PROCESS_CAPABILITY_ALL, 0, false); + assertEquals("expected=ACTIVE,actual=" + changeToStr(change), + UidRecord.CHANGE_ACTIVE, change); + assertPendingChange(TEST_UID1, UidRecord.CHANGE_ACTIVE, PROCESS_STATE_FOREGROUND_SERVICE, + PROCESS_CAPABILITY_ALL, 0, false, null); + final ChangeRecord record1 = getLatestPendingChange(TEST_UID1); + assertNull(getLatestPendingChange(TEST_UID2)); + + final ChangeRecord record2 = new ChangeRecord(); + change = mUidObserverController.enqueueUidChange(record2, TEST_UID2, + UidRecord.CHANGE_CACHED, PROCESS_STATE_CACHED_RECENT, PROCESS_CAPABILITY_NONE, + 99, true); + assertEquals("expected=ACTIVE,actual=" + changeToStr(change), + UidRecord.CHANGE_CACHED, change); + assertPendingChange(TEST_UID1, UidRecord.CHANGE_ACTIVE, PROCESS_STATE_FOREGROUND_SERVICE, + PROCESS_CAPABILITY_ALL, 0, false, null); + assertPendingChange(TEST_UID2, UidRecord.CHANGE_CACHED, PROCESS_STATE_CACHED_RECENT, + PROCESS_CAPABILITY_NONE, 99, true, record2); + + change = mUidObserverController.enqueueUidChange(record1, TEST_UID1, + UidRecord.CHANGE_UNCACHED, PROCESS_STATE_TOP, PROCESS_CAPABILITY_ALL, 0, false); + assertEquals("expected=ACTIVE|UNCACHED,actual=" + changeToStr(change), + UidRecord.CHANGE_ACTIVE | UidRecord.CHANGE_UNCACHED, change); + assertPendingChange(TEST_UID1, UidRecord.CHANGE_ACTIVE | UidRecord.CHANGE_UNCACHED, + PROCESS_STATE_TOP, PROCESS_CAPABILITY_ALL, 0, false, record1); + assertPendingChange(TEST_UID2, UidRecord.CHANGE_CACHED, PROCESS_STATE_CACHED_RECENT, + PROCESS_CAPABILITY_NONE, 99, true, record2); + } + + @Test + public void testMergeWithPendingChange() { + final SparseArray<Pair<Integer, Integer>> changesToVerify = new SparseArray<>(); + + changesToVerify.put(UidRecord.CHANGE_ACTIVE, + Pair.create(UidRecord.CHANGE_ACTIVE, UidRecord.CHANGE_IDLE)); + changesToVerify.put(UidRecord.CHANGE_IDLE, + Pair.create(UidRecord.CHANGE_IDLE, UidRecord.CHANGE_ACTIVE)); + changesToVerify.put(UidRecord.CHANGE_CACHED, + Pair.create(UidRecord.CHANGE_CACHED, UidRecord.CHANGE_UNCACHED)); + changesToVerify.put(UidRecord.CHANGE_UNCACHED, + Pair.create(UidRecord.CHANGE_UNCACHED, UidRecord.CHANGE_CACHED)); + changesToVerify.put(UidRecord.CHANGE_ACTIVE | UidRecord.CHANGE_UNCACHED, + Pair.create(UidRecord.CHANGE_ACTIVE, UidRecord.CHANGE_UNCACHED)); + changesToVerify.put(UidRecord.CHANGE_IDLE | UidRecord.CHANGE_CACHED, + Pair.create(UidRecord.CHANGE_IDLE, UidRecord.CHANGE_CACHED)); + changesToVerify.put(UidRecord.CHANGE_GONE, + Pair.create(UidRecord.CHANGE_GONE, UidRecord.CHANGE_ACTIVE)); + changesToVerify.put(UidRecord.CHANGE_GONE, + Pair.create(UidRecord.CHANGE_GONE, UidRecord.CHANGE_CACHED)); + + for (int i = 0; i < changesToVerify.size(); ++i) { + final int expectedChange = changesToVerify.keyAt(i); + final int currentChange = changesToVerify.valueAt(i).first; + final int pendingChange = changesToVerify.valueAt(i).second; + assertEquals("current=" + changeToStr(currentChange) + ", pending=" + + changeToStr(pendingChange) + "exp=" + changeToStr(expectedChange), + expectedChange, UidObserverController.mergeWithPendingChange( + currentChange, pendingChange)); + } + } + + @Test + public void testDispatchUidsChanged() throws RemoteException { + addPendingChange(TEST_UID1, UidRecord.CHANGE_ACTIVE | UidRecord.CHANGE_PROCSTATE, + PROCESS_STATE_TOP, 0, PROCESS_CAPABILITY_ALL, false); + + final IUidObserver observer1 = mock(IUidObserver.Stub.class); + registerObserver(observer1, + ActivityManager.UID_OBSERVER_PROCSTATE | ActivityManager.UID_OBSERVER_ACTIVE, + PROCESS_STATE_IMPORTANT_FOREGROUND, TEST_PKG2, TEST_UID2); + final IUidObserver observer2 = mock(IUidObserver.Stub.class); + registerObserver(observer2, ActivityManager.UID_OBSERVER_PROCSTATE, + PROCESS_STATE_SERVICE, TEST_PKG3, TEST_UID3); + + mUidObserverController.dispatchUidsChanged(); + verify(observer1).onUidStateChanged(TEST_UID1, PROCESS_STATE_TOP, + 0, PROCESS_CAPABILITY_ALL); + verify(observer1).onUidActive(TEST_UID1); + verifyNoMoreInteractions(observer1); + verify(observer2).onUidStateChanged(TEST_UID1, PROCESS_STATE_TOP, + 0, PROCESS_CAPABILITY_ALL); + verifyNoMoreInteractions(observer2); + + addPendingChange(TEST_UID1, UidRecord.CHANGE_PROCSTATE, PROCESS_STATE_IMPORTANT_BACKGROUND, + 99, PROCESS_CAPABILITY_FOREGROUND_LOCATION, false); + mUidObserverController.dispatchUidsChanged(); + verify(observer1).onUidStateChanged(TEST_UID1, PROCESS_STATE_IMPORTANT_BACKGROUND, + 99, PROCESS_CAPABILITY_FOREGROUND_LOCATION); + verifyNoMoreInteractions(observer1); + verifyNoMoreInteractions(observer2); + + addPendingChange(TEST_UID1, UidRecord.CHANGE_PROCSTATE, PROCESS_STATE_RECEIVER, + 111, PROCESS_CAPABILITY_NONE, false); + mUidObserverController.dispatchUidsChanged(); + verify(observer2).onUidStateChanged(TEST_UID1, PROCESS_STATE_RECEIVER, + 111, PROCESS_CAPABILITY_NONE); + verifyNoMoreInteractions(observer1); + verifyNoMoreInteractions(observer2); + + unregisterObserver(observer1); + + addPendingChange(TEST_UID1, UidRecord.CHANGE_PROCSTATE, PROCESS_STATE_TOP, + 112, PROCESS_CAPABILITY_ALL, false); + mUidObserverController.dispatchUidsChanged(); + verify(observer2).onUidStateChanged(TEST_UID1, PROCESS_STATE_TOP, + 112, PROCESS_CAPABILITY_ALL); + verifyNoMoreInteractions(observer1); + verifyNoMoreInteractions(observer2); + + unregisterObserver(observer2); + + addPendingChange(TEST_UID1, UidRecord.CHANGE_PROCSTATE, PROCESS_STATE_CACHED_RECENT, + 112, PROCESS_CAPABILITY_NONE, false); + mUidObserverController.dispatchUidsChanged(); + verifyNoMoreInteractions(observer1); + verifyNoMoreInteractions(observer2); + } + + private void registerObserver(IUidObserver observer, int which, int cutpoint, + String callingPackage, int callingUid) { + when(observer.asBinder()).thenReturn((IBinder) observer); + mUidObserverController.register(observer, which, cutpoint, callingPackage, callingUid); + Mockito.reset(observer); + } + + private void unregisterObserver(IUidObserver observer) { + when(observer.asBinder()).thenReturn((IBinder) observer); + mUidObserverController.unregister(observer); + Mockito.reset(observer); + } + + private void addPendingChange(int uid, int change, int procState, long procStateSeq, + int capability, boolean ephemeral) { + final ChangeRecord record = new ChangeRecord(); + record.uid = uid; + record.change = change; + record.procState = procState; + record.procStateSeq = procStateSeq; + record.capability = capability; + record.ephemeral = ephemeral; + mUidObserverController.getPendingUidChangesForTest().add(record); + } + + private void assertPendingChange(int uid, int change, int procState, long procStateSeq, + int capability, boolean ephemeral, ChangeRecord expectedRecord) { + final ChangeRecord record = getLatestPendingChange(uid); + assertNotNull(record); + if (expectedRecord != null) { + assertEquals(expectedRecord, record); + } + assertEquals(change, record.change); + assertEquals(procState, record.procState); + assertEquals(procStateSeq, record.procStateSeq); + assertEquals(capability, record.capability); + assertEquals(ephemeral, record.ephemeral); + } + + private ChangeRecord getLatestPendingChange(int uid) { + final ArrayList<ChangeRecord> changeRecords = mUidObserverController + .getPendingUidChangesForTest(); + for (int i = changeRecords.size() - 1; i >= 0; --i) { + final ChangeRecord record = changeRecords.get(i); + if (record.uid == uid) { + return record; + } + } + return null; + } + + private static String changeToStr(int change) { + return DebugUtils.flagsToString(UidRecord.class, "CHANGE_", change); + } +} diff --git a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java index 6f4ff35ba0df..04de6ca192ef 100644 --- a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java @@ -153,7 +153,7 @@ public class UserControllerTest { doNothing().when(mInjector).startHomeActivity(anyInt(), anyString()); doReturn(false).when(mInjector).stackSupervisorSwitchUser(anyInt(), any()); doNothing().when(mInjector).stackSupervisorResumeFocusedStackTopActivity(); - doNothing().when(mInjector).systemServiceManagerCleanupUser(anyInt()); + doNothing().when(mInjector).systemServiceManagerOnUserStopped(anyInt()); doNothing().when(mInjector).activityManagerForceStopPackage(anyInt(), anyString()); doNothing().when(mInjector).activityManagerOnUserStopped(anyInt()); doNothing().when(mInjector).clearBroadcastQueueForUser(anyInt()); diff --git a/services/tests/servicestests/src/com/android/server/appsearch/external/localbackend/AppSearchImplTest.java b/services/tests/servicestests/src/com/android/server/appsearch/external/localbackend/AppSearchImplTest.java index 24f78308c600..081bfb5b2724 100644 --- a/services/tests/servicestests/src/com/android/server/appsearch/external/localbackend/AppSearchImplTest.java +++ b/services/tests/servicestests/src/com/android/server/appsearch/external/localbackend/AppSearchImplTest.java @@ -50,8 +50,7 @@ public class AppSearchImplTest { @Before public void setUp() throws Exception { - mAppSearchImpl = new AppSearchImpl(mTemporaryFolder.newFolder()); - mAppSearchImpl.initialize(); + mAppSearchImpl = AppSearchImpl.create(mTemporaryFolder.newFolder()); } /** @@ -288,7 +287,8 @@ public class AppSearchImplTest { SchemaProto finalSchemaProto = schemaProto; AppSearchException e = expectThrows(AppSearchException.class, () -> mAppSearchImpl.setSchema("database1", finalSchemaProto, /*forceOverride=*/false)); - assertThat(e).hasMessageThat().isEqualTo("Schema is incompatible."); + assertThat(e).hasMessageThat().contains("Schema is incompatible"); + assertThat(e).hasMessageThat().contains("Deleted types: [database1/Document]"); // ForceOverride to delete. mAppSearchImpl.setSchema("database1", finalSchemaProto, /*forceOverride=*/true); diff --git a/services/tests/servicestests/src/com/android/server/appsearch/external/localbackend/converter/GenericDocumentToProtoConverterTest.java b/services/tests/servicestests/src/com/android/server/appsearch/external/localbackend/converter/GenericDocumentToProtoConverterTest.java new file mode 100644 index 000000000000..a95290d89b59 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/appsearch/external/localbackend/converter/GenericDocumentToProtoConverterTest.java @@ -0,0 +1,104 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.appsearch.external.localbackend.converter; + +import static com.google.common.truth.Truth.assertThat; + +import android.app.appsearch.GenericDocument; + +import com.android.server.appsearch.proto.DocumentProto; +import com.android.server.appsearch.proto.PropertyProto; +import com.android.server.appsearch.protobuf.ByteString; + +import org.junit.Test; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; + +public class GenericDocumentToProtoConverterTest { + private static final byte[] BYTE_ARRAY_1 = new byte[]{(byte) 1, (byte) 2, (byte) 3}; + private static final byte[] BYTE_ARRAY_2 = new byte[]{(byte) 4, (byte) 5, (byte) 6, (byte) 7}; + private static final GenericDocument DOCUMENT_PROPERTIES_1 = + new GenericDocument.Builder<GenericDocument.Builder<?>>( + "sDocumentProperties1", "sDocumentPropertiesSchemaType1") + .setCreationTimestampMillis(12345L) + .build(); + private static final GenericDocument DOCUMENT_PROPERTIES_2 = + new GenericDocument.Builder<GenericDocument.Builder<?>>( + "sDocumentProperties2", "sDocumentPropertiesSchemaType2") + .setCreationTimestampMillis(6789L) + .build(); + + @Test + public void testDocumentProtoConvert() { + GenericDocument document = + new GenericDocument.Builder<GenericDocument.Builder<?>>("uri1", "schemaType1") + .setCreationTimestampMillis(5L) + .setScore(1) + .setTtlMillis(1L) + .setNamespace("namespace") + .setProperty("longKey1", 1L) + .setProperty("doubleKey1", 1.0) + .setProperty("booleanKey1", true) + .setProperty("stringKey1", "test-value1") + .setProperty("byteKey1", BYTE_ARRAY_1, BYTE_ARRAY_2) + .setProperty("documentKey1", DOCUMENT_PROPERTIES_1) + .setProperty(GenericDocument.PROPERTIES_FIELD, DOCUMENT_PROPERTIES_2) + .build(); + + // Create the Document proto. Need to sort the property order by key. + DocumentProto.Builder documentProtoBuilder = DocumentProto.newBuilder() + .setUri("uri1") + .setSchema("schemaType1") + .setCreationTimestampMs(5L) + .setScore(1) + .setTtlMs(1L) + .setNamespace("namespace"); + HashMap<String, PropertyProto.Builder> propertyProtoMap = new HashMap<>(); + propertyProtoMap.put("longKey1", + PropertyProto.newBuilder().setName("longKey1").addInt64Values(1L)); + propertyProtoMap.put("doubleKey1", + PropertyProto.newBuilder().setName("doubleKey1").addDoubleValues(1.0)); + propertyProtoMap.put("booleanKey1", + PropertyProto.newBuilder().setName("booleanKey1").addBooleanValues(true)); + propertyProtoMap.put("stringKey1", + PropertyProto.newBuilder().setName("stringKey1").addStringValues("test-value1")); + propertyProtoMap.put("byteKey1", + PropertyProto.newBuilder().setName("byteKey1") + .addBytesValues(ByteString.copyFrom(BYTE_ARRAY_1)) + .addBytesValues(ByteString.copyFrom(BYTE_ARRAY_2))); + propertyProtoMap.put("documentKey1", + PropertyProto.newBuilder().setName("documentKey1") + .addDocumentValues( + GenericDocumentToProtoConverter.convert(DOCUMENT_PROPERTIES_1))); + propertyProtoMap.put(GenericDocument.PROPERTIES_FIELD, + PropertyProto.newBuilder().setName(GenericDocument.PROPERTIES_FIELD) + .addDocumentValues( + GenericDocumentToProtoConverter.convert(DOCUMENT_PROPERTIES_2))); + List<String> sortedKey = new ArrayList<>(propertyProtoMap.keySet()); + Collections.sort(sortedKey); + for (String key : sortedKey) { + documentProtoBuilder.addProperties(propertyProtoMap.get(key)); + } + DocumentProto documentProto = documentProtoBuilder.build(); + assertThat(GenericDocumentToProtoConverter.convert(document)) + .isEqualTo(documentProto); + assertThat(document).isEqualTo(GenericDocumentToProtoConverter.convert(documentProto)); + } +} diff --git a/core/tests/coretests/src/android/app/appsearch/SnippetTest.java b/services/tests/servicestests/src/com/android/server/appsearch/external/localbackend/converter/SnippetTest.java index 95f5b105402c..e9357aa60632 100644 --- a/core/tests/coretests/src/android/app/appsearch/SnippetTest.java +++ b/services/tests/servicestests/src/com/android/server/appsearch/external/localbackend/converter/SnippetTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2019 The Android Open Source Project + * Copyright 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,24 +14,23 @@ * limitations under the License. */ -package android.app.appsearch; +package com.android.server.appsearch.external.localbackend.converter; import static com.google.common.truth.Truth.assertThat; -import android.app.appsearch.proto.DocumentProto; -import android.app.appsearch.proto.PropertyProto; -import android.app.appsearch.proto.SearchResultProto; -import android.app.appsearch.proto.SnippetMatchProto; -import android.app.appsearch.proto.SnippetProto; +import android.app.appsearch.SearchResult; -import androidx.test.filters.SmallTest; +import com.android.server.appsearch.proto.DocumentProto; +import com.android.server.appsearch.proto.PropertyProto; +import com.android.server.appsearch.proto.SearchResultProto; +import com.android.server.appsearch.proto.SnippetMatchProto; +import com.android.server.appsearch.proto.SnippetProto; import org.junit.Test; -@SmallTest public class SnippetTest { - // TODO(sidchhabra): Add tests for Double and Long Snippets. + // TODO(tytytyww): Add tests for Double and Long Snippets. @Test public void testSingleStringSnippet() { @@ -74,22 +73,26 @@ public class SnippetTest { SearchResultProto searchResultProto = SearchResultProto.newBuilder() .addResults(resultProto) .build(); - SearchResults searchResults = new SearchResults(searchResultProto); // Making ResultReader and getting Snippet values. - while (searchResults.hasNext()) { - SearchResults.Result result = searchResults.next(); - MatchInfo match = result.getMatchInfo().get(0); + for (SearchResultProto.ResultProto proto : searchResultProto.getResultsList()) { + SearchResult result = SearchResultToProtoConverter.convertSearchResult(proto); + SearchResult.MatchInfo match = result.getMatches().get(0); assertThat(match.getPropertyPath()).isEqualTo(propertyKeyString); assertThat(match.getFullText()).isEqualTo(propertyValueString); assertThat(match.getExactMatch()).isEqualTo(exactMatch); + assertThat(match.getExactMatchPosition()).isEqualTo( + new SearchResult.MatchRange(/*lower=*/29, /*upper=*/32)); + assertThat(match.getFullText()).isEqualTo(propertyValueString); + assertThat(match.getSnippetPosition()).isEqualTo( + new SearchResult.MatchRange(/*lower=*/26, /*upper=*/32)); assertThat(match.getSnippet()).isEqualTo(window); } } - // TODO(sidchhabra): Add tests for Double and Long Snippets. + // TODO(tytytyww): Add tests for Double and Long Snippets. @Test - public void testNoSnippets() { + public void testNoSnippets() throws Exception { final String propertyKeyString = "content"; final String propertyValueString = "A commonly used fake word is foo.\n" @@ -117,16 +120,15 @@ public class SnippetTest { SearchResultProto searchResultProto = SearchResultProto.newBuilder() .addResults(resultProto) .build(); - SearchResults searchResults = new SearchResults(searchResultProto); - while (searchResults.hasNext()) { - SearchResults.Result result = searchResults.next(); - assertThat(result.getMatchInfo()).isEqualTo(null); + for (SearchResultProto.ResultProto proto : searchResultProto.getResultsList()) { + SearchResult result = SearchResultToProtoConverter.convertSearchResult(proto); + assertThat(result.getMatches()).isEqualTo(null); } } @Test - public void testMultipleStringSnippet() { + public void testMultipleStringSnippet() throws Exception { final String searchWord = "Test"; // Building the SearchResult received from query. @@ -178,22 +180,29 @@ public class SnippetTest { SearchResultProto searchResultProto = SearchResultProto.newBuilder() .addResults(resultProto) .build(); - SearchResults searchResults = new SearchResults(searchResultProto); // Making ResultReader and getting Snippet values. - while (searchResults.hasNext()) { - SearchResults.Result result = searchResults.next(); + for (SearchResultProto.ResultProto proto : searchResultProto.getResultsList()) { + SearchResult result = SearchResultToProtoConverter.convertSearchResult(proto); - MatchInfo match1 = result.getMatchInfo().get(0); + SearchResult.MatchInfo match1 = result.getMatches().get(0); assertThat(match1.getPropertyPath()).isEqualTo("sender.name"); assertThat(match1.getFullText()).isEqualTo("Test Name Jr."); + assertThat(match1.getExactMatchPosition()).isEqualTo( + new SearchResult.MatchRange(/*lower=*/0, /*upper=*/4)); assertThat(match1.getExactMatch()).isEqualTo("Test"); + assertThat(match1.getSnippetPosition()).isEqualTo( + new SearchResult.MatchRange(/*lower=*/0, /*upper=*/9)); assertThat(match1.getSnippet()).isEqualTo("Test Name"); - MatchInfo match2 = result.getMatchInfo().get(1); + SearchResult.MatchInfo match2 = result.getMatches().get(1); assertThat(match2.getPropertyPath()).isEqualTo("sender.email"); assertThat(match2.getFullText()).isEqualTo("TestNameJr@gmail.com"); + assertThat(match2.getExactMatchPosition()).isEqualTo( + new SearchResult.MatchRange(/*lower=*/0, /*upper=*/20)); assertThat(match2.getExactMatch()).isEqualTo("TestNameJr@gmail.com"); + assertThat(match2.getSnippetPosition()).isEqualTo( + new SearchResult.MatchRange(/*lower=*/0, /*upper=*/20)); assertThat(match2.getSnippet()).isEqualTo("TestNameJr@gmail.com"); } } diff --git a/services/tests/servicestests/src/com/android/server/compat/CompatConfigBuilder.java b/services/tests/servicestests/src/com/android/server/compat/CompatConfigBuilder.java index 2cbe7be8ac33..870fe4a0837e 100644 --- a/services/tests/servicestests/src/com/android/server/compat/CompatConfigBuilder.java +++ b/services/tests/servicestests/src/com/android/server/compat/CompatConfigBuilder.java @@ -39,58 +39,79 @@ class CompatConfigBuilder { return new CompatConfigBuilder(buildClassifier, context); } - CompatConfigBuilder addTargetSdkChangeWithId(int sdk, long id) { - mChanges.add(new CompatChange(id, "", sdk, false, false, "")); + CompatConfigBuilder addEnableAfterSdkChangeWithId(int sdk, long id) { + mChanges.add(new CompatChange(id, "", sdk, -1, false, false, "")); return this; } - CompatConfigBuilder addTargetSdkDisabledChangeWithId(int sdk, long id) { - mChanges.add(new CompatChange(id, "", sdk, true, false, "")); + CompatConfigBuilder addEnableAfterSdkChangeWithIdAndName(int sdk, long id, String name) { + mChanges.add(new CompatChange(id, name, sdk, -1, false, false, "")); return this; } - CompatConfigBuilder addTargetSdkChangeWithIdAndName(int sdk, long id, String name) { - mChanges.add(new CompatChange(id, name, sdk, false, false, "")); + CompatConfigBuilder addEnableAfterSdkChangeWithIdDefaultDisabled(int sdk, long id) { + mChanges.add(new CompatChange(id, "", sdk, -1, true, false, "")); return this; } - CompatConfigBuilder addTargetSdkChangeWithIdAndDescription(int sdk, long id, + CompatConfigBuilder addEnableAfterSdkChangeWithIdAndDescription(int sdk, long id, String description) { - mChanges.add(new CompatChange(id, "", sdk, false, false, description)); + mChanges.add(new CompatChange(id, "", sdk, -1, false, false, description)); + return this; + } + + CompatConfigBuilder addEnableSinceSdkChangeWithId(int sdk, long id) { + mChanges.add(new CompatChange(id, "", -1, sdk, false, false, "")); + return this; + } + + CompatConfigBuilder addEnableSinceSdkChangeWithIdAndName(int sdk, long id, String name) { + mChanges.add(new CompatChange(id, name, -1, sdk, false, false, "")); + return this; + } + + CompatConfigBuilder addEnableSinceSdkChangeWithIdDefaultDisabled(int sdk, long id) { + mChanges.add(new CompatChange(id, "", -1, sdk, true, false, "")); + return this; + } + + CompatConfigBuilder addEnableSinceSdkChangeWithIdAndDescription(int sdk, long id, + String description) { + mChanges.add(new CompatChange(id, "", -1, sdk, false, false, description)); return this; } CompatConfigBuilder addEnabledChangeWithId(long id) { - mChanges.add(new CompatChange(id, "", -1, false, false, "")); + mChanges.add(new CompatChange(id, "", -1, -1, false, false, "")); return this; } CompatConfigBuilder addEnabledChangeWithIdAndName(long id, String name) { - mChanges.add(new CompatChange(id, name, -1, false, false, "")); + mChanges.add(new CompatChange(id, name, -1, -1, false, false, "")); return this; } CompatConfigBuilder addEnabledChangeWithIdAndDescription(long id, String description) { - mChanges.add(new CompatChange(id, "", -1, false, false, description)); + mChanges.add(new CompatChange(id, "", -1, -1, false, false, description)); return this; } CompatConfigBuilder addDisabledChangeWithId(long id) { - mChanges.add(new CompatChange(id, "", -1, true, false, "")); + mChanges.add(new CompatChange(id, "", -1, -1, true, false, "")); return this; } CompatConfigBuilder addDisabledChangeWithIdAndName(long id, String name) { - mChanges.add(new CompatChange(id, name, -1, true, false, "")); + mChanges.add(new CompatChange(id, name, -1, -1, true, false, "")); return this; } CompatConfigBuilder addDisabledChangeWithIdAndDescription(long id, String description) { - mChanges.add(new CompatChange(id, "", -1, true, false, description)); + mChanges.add(new CompatChange(id, "", -1, -1, true, false, description)); return this; } CompatConfigBuilder addLoggingOnlyChangeWithId(long id) { - mChanges.add(new CompatChange(id, "", -1, false, true, "")); + mChanges.add(new CompatChange(id, "", -1, -1, false, true, "")); return this; } diff --git a/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java b/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java index 8be9213fd925..e5883708e921 100644 --- a/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java +++ b/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java @@ -98,7 +98,7 @@ public class CompatConfigTest { @Test public void testTargetSdkChangeDisabled() throws Exception { CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext) - .addTargetSdkChangeWithId(2, 1234L) + .addEnableAfterSdkChangeWithId(2, 1234L) .build(); assertThat(compatConfig.isChangeEnabled(1234L, @@ -109,7 +109,7 @@ public class CompatConfigTest { @Test public void testTargetSdkChangeEnabled() throws Exception { CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext) - .addTargetSdkChangeWithId(2, 1234L) + .addEnableAfterSdkChangeWithId(2, 1234L) .build(); assertThat(compatConfig.isChangeEnabled(1234L, @@ -119,7 +119,7 @@ public class CompatConfigTest { @Test public void testDisabledOverrideTargetSdkChange() throws Exception { CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext) - .addTargetSdkDisabledChangeWithId(2, 1234L) + .addEnableAfterSdkChangeWithIdDefaultDisabled(2, 1234L) .build(); assertThat(compatConfig.isChangeEnabled(1234L, @@ -293,8 +293,8 @@ public class CompatConfigTest { CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext) .addEnabledChangeWithId(1L) .addDisabledChangeWithId(2L) - .addTargetSdkChangeWithId(3, 3L) - .addTargetSdkChangeWithId(4, 4L) + .addEnableSinceSdkChangeWithId(3, 3L) + .addEnableSinceSdkChangeWithId(4, 4L) .build(); ApplicationInfo applicationInfo = ApplicationInfoBuilder.create() .withPackageName("foo.bar") @@ -314,8 +314,8 @@ public class CompatConfigTest { CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext) .addEnabledChangeWithId(1L) .addDisabledChangeWithId(2L) - .addTargetSdkChangeWithId(3, 3L) - .addTargetSdkChangeWithId(4, 4L) + .addEnableSinceSdkChangeWithId(3, 3L) + .addEnableSinceSdkChangeWithId(4, 4L) .build(); ApplicationInfo applicationInfo = ApplicationInfoBuilder.create() .withPackageName("foo.bar") diff --git a/services/tests/servicestests/src/com/android/server/compat/OverrideValidatorImplTest.java b/services/tests/servicestests/src/com/android/server/compat/OverrideValidatorImplTest.java index d45589d90ce3..c53b29a08a4a 100644 --- a/services/tests/servicestests/src/com/android/server/compat/OverrideValidatorImplTest.java +++ b/services/tests/servicestests/src/com/android/server/compat/OverrideValidatorImplTest.java @@ -87,9 +87,9 @@ public class OverrideValidatorImplTest { public void getOverrideAllowedState_debugBuildAnyChangeDebugApp_allowOverride() throws Exception { CompatConfig config = CompatConfigBuilder.create(debuggableBuild(), mContext) - .addTargetSdkChangeWithId(TARGET_SDK_BEFORE, 1) - .addTargetSdkChangeWithId(TARGET_SDK, 2) - .addTargetSdkChangeWithId(TARGET_SDK_AFTER, 3) + .addEnableAfterSdkChangeWithId(TARGET_SDK_BEFORE, 1) + .addEnableAfterSdkChangeWithId(TARGET_SDK, 2) + .addEnableAfterSdkChangeWithId(TARGET_SDK_AFTER, 3) .addEnabledChangeWithId(4) .addDisabledChangeWithId(5) .addLoggingOnlyChangeWithId(6).build(); @@ -131,9 +131,9 @@ public class OverrideValidatorImplTest { public void getOverrideAllowedState_debugBuildAnyChangeReleaseApp_allowOverride() throws Exception { CompatConfig config = CompatConfigBuilder.create(debuggableBuild(), mContext) - .addTargetSdkChangeWithId(TARGET_SDK_BEFORE, 1) - .addTargetSdkChangeWithId(TARGET_SDK, 2) - .addTargetSdkChangeWithId(TARGET_SDK_AFTER, 3) + .addEnableAfterSdkChangeWithId(TARGET_SDK_BEFORE, 1) + .addEnableAfterSdkChangeWithId(TARGET_SDK, 2) + .addEnableAfterSdkChangeWithId(TARGET_SDK_AFTER, 3) .addEnabledChangeWithId(4) .addDisabledChangeWithId(5) .addLoggingOnlyChangeWithId(6).build(); @@ -174,9 +174,9 @@ public class OverrideValidatorImplTest { public void getOverrideAllowedState_betaBuildTargetSdkChangeDebugApp_allowOverride() throws Exception { CompatConfig config = CompatConfigBuilder.create(betaBuild(), mContext) - .addTargetSdkChangeWithId(TARGET_SDK_BEFORE, 1) - .addTargetSdkChangeWithId(TARGET_SDK, 2) - .addTargetSdkChangeWithId(TARGET_SDK_AFTER, 3) + .addEnableAfterSdkChangeWithId(TARGET_SDK_BEFORE, 1) + .addEnableAfterSdkChangeWithId(TARGET_SDK, 2) + .addEnableAfterSdkChangeWithId(TARGET_SDK_AFTER, 3) .addDisabledChangeWithId(4).build(); IOverrideValidator overrideValidator = config.getOverrideValidator(); when(mPackageManager.getApplicationInfo(eq(PACKAGE_NAME), anyInt())) @@ -245,9 +245,9 @@ public class OverrideValidatorImplTest { public void getOverrideAllowedState_betaBuildAnyChangeReleaseApp_rejectOverride() throws Exception { CompatConfig config = CompatConfigBuilder.create(betaBuild(), mContext) - .addTargetSdkChangeWithId(TARGET_SDK_BEFORE, 1) - .addTargetSdkChangeWithId(TARGET_SDK, 2) - .addTargetSdkChangeWithId(TARGET_SDK_AFTER, 3) + .addEnableAfterSdkChangeWithId(TARGET_SDK_BEFORE, 1) + .addEnableAfterSdkChangeWithId(TARGET_SDK, 2) + .addEnableAfterSdkChangeWithId(TARGET_SDK_AFTER, 3) .addEnabledChangeWithId(4) .addDisabledChangeWithId(5) .addLoggingOnlyChangeWithId(6).build(); @@ -288,8 +288,8 @@ public class OverrideValidatorImplTest { public void getOverrideAllowedState_finalBuildTargetSdkChangeDebugAppOptin_allowOverride() throws Exception { CompatConfig config = CompatConfigBuilder.create(finalBuild(), mContext) - .addTargetSdkChangeWithId(TARGET_SDK_AFTER, 1) - .addTargetSdkChangeWithId(TARGET_SDK, 2).build(); + .addEnableAfterSdkChangeWithId(TARGET_SDK_AFTER, 1) + .addEnableAfterSdkChangeWithId(TARGET_SDK, 2).build(); IOverrideValidator overrideValidator = config.getOverrideValidator(); when(mPackageManager.getApplicationInfo(eq(PACKAGE_NAME), anyInt())) .thenReturn(ApplicationInfoBuilder.create() @@ -313,7 +313,7 @@ public class OverrideValidatorImplTest { public void getOverrideAllowedState_finalBuildTargetSdkChangeDebugAppOptout_rejectOverride() throws Exception { CompatConfig config = CompatConfigBuilder.create(finalBuild(), mContext) - .addTargetSdkChangeWithId(TARGET_SDK_BEFORE, 1).build(); + .addEnableAfterSdkChangeWithId(TARGET_SDK_BEFORE, 1).build(); IOverrideValidator overrideValidator = config.getOverrideValidator(); when(mPackageManager.getApplicationInfo(eq(PACKAGE_NAME), anyInt())) .thenReturn(ApplicationInfoBuilder.create() @@ -371,9 +371,9 @@ public class OverrideValidatorImplTest { public void getOverrideAllowedState_finalBuildAnyChangeReleaseApp_rejectOverride() throws Exception { CompatConfig config = CompatConfigBuilder.create(finalBuild(), mContext) - .addTargetSdkChangeWithId(TARGET_SDK_BEFORE, 1) - .addTargetSdkChangeWithId(TARGET_SDK, 2) - .addTargetSdkChangeWithId(TARGET_SDK_AFTER, 3) + .addEnableAfterSdkChangeWithId(TARGET_SDK_BEFORE, 1) + .addEnableAfterSdkChangeWithId(TARGET_SDK, 2) + .addEnableAfterSdkChangeWithId(TARGET_SDK_AFTER, 3) .addEnabledChangeWithId(4) .addDisabledChangeWithId(5) .addLoggingOnlyChangeWithId(6).build(); diff --git a/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java b/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java index cef02ffc3831..64014ba182d2 100644 --- a/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java +++ b/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java @@ -84,22 +84,22 @@ public class PlatformCompatTest { mCompatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext) .addEnabledChangeWithId(1L) .addDisabledChangeWithIdAndName(2L, "change2") - .addTargetSdkChangeWithIdAndDescription(Build.VERSION_CODES.O, 3L, "description") - .addTargetSdkChangeWithId(Build.VERSION_CODES.P, 4L) - .addTargetSdkChangeWithId(Build.VERSION_CODES.Q, 5L) - .addTargetSdkChangeWithId(Build.VERSION_CODES.R, 6L) + .addEnableAfterSdkChangeWithIdAndDescription(Build.VERSION_CODES.O, 3L, "desc") + .addEnableAfterSdkChangeWithId(Build.VERSION_CODES.P, 4L) + .addEnableAfterSdkChangeWithId(Build.VERSION_CODES.Q, 5L) + .addEnableAfterSdkChangeWithId(Build.VERSION_CODES.R, 6L) .addLoggingOnlyChangeWithId(7L) .build(); mPlatformCompat = new PlatformCompat(mContext, mCompatConfig); assertThat(mPlatformCompat.listAllChanges()).asList().containsExactly( - new CompatibilityChangeInfo(1L, "", -1, false, false, ""), - new CompatibilityChangeInfo(2L, "change2", -1, true, false, ""), - new CompatibilityChangeInfo(3L, "", Build.VERSION_CODES.O, false, false, - "description"), - new CompatibilityChangeInfo(4L, "", Build.VERSION_CODES.P, false, false, ""), - new CompatibilityChangeInfo(5L, "", Build.VERSION_CODES.Q, false, false, ""), - new CompatibilityChangeInfo(6L, "", Build.VERSION_CODES.R, false, false, ""), - new CompatibilityChangeInfo(7L, "", -1, false, true, "")); + new CompatibilityChangeInfo(1L, "", -1, -1, false, false, ""), + new CompatibilityChangeInfo(2L, "change2", -1, -1, true, false, ""), + new CompatibilityChangeInfo(3L, "", Build.VERSION_CODES.O, -1, false, false, + "desc"), + new CompatibilityChangeInfo(4L, "", Build.VERSION_CODES.P, -1, false, false, ""), + new CompatibilityChangeInfo(5L, "", Build.VERSION_CODES.Q, -1, false, false, ""), + new CompatibilityChangeInfo(6L, "", Build.VERSION_CODES.R, -1, false, false, ""), + new CompatibilityChangeInfo(7L, "", -1, -1, false, true, "")); } @Test @@ -107,18 +107,18 @@ public class PlatformCompatTest { mCompatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext) .addEnabledChangeWithId(1L) .addDisabledChangeWithIdAndName(2L, "change2") - .addTargetSdkChangeWithIdAndDescription(Build.VERSION_CODES.O, 3L, "description") - .addTargetSdkChangeWithId(Build.VERSION_CODES.P, 4L) - .addTargetSdkChangeWithId(Build.VERSION_CODES.Q, 5L) - .addTargetSdkChangeWithId(Build.VERSION_CODES.R, 6L) + .addEnableAfterSdkChangeWithIdAndDescription(Build.VERSION_CODES.O, 3L, "desc") + .addEnableAfterSdkChangeWithId(Build.VERSION_CODES.P, 4L) + .addEnableAfterSdkChangeWithId(Build.VERSION_CODES.Q, 5L) + .addEnableAfterSdkChangeWithId(Build.VERSION_CODES.R, 6L) .addLoggingOnlyChangeWithId(7L) .build(); mPlatformCompat = new PlatformCompat(mContext, mCompatConfig); assertThat(mPlatformCompat.listUIChanges()).asList().containsExactly( - new CompatibilityChangeInfo(1L, "", -1, false, false, ""), - new CompatibilityChangeInfo(2L, "change2", -1, true, false, ""), - new CompatibilityChangeInfo(4L, "", Build.VERSION_CODES.P, false, false, ""), - new CompatibilityChangeInfo(5L, "", Build.VERSION_CODES.Q, false, false, "")); + new CompatibilityChangeInfo(1L, "", -1, -1, false, false, ""), + new CompatibilityChangeInfo(2L, "change2", -1, -1, true, false, ""), + new CompatibilityChangeInfo(4L, "", Build.VERSION_CODES.P, -1, false, false, ""), + new CompatibilityChangeInfo(5L, "", Build.VERSION_CODES.Q, -1, false, false, "")); } @Test 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 631b4d48e9f2..30b1b3e78ad3 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java @@ -5728,6 +5728,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { // Device owner should be allowed to request Device ID attestation. dpms.enforceCallerCanRequestDeviceIdAttestation(dpms.getCallerIdentity(admin1)); + mContext.binder.callingUid = DpmMockContext.ANOTHER_UID; // Another package must not be allowed to request Device ID attestation. assertExpectException(SecurityException.class, null, () -> dpms.enforceCallerCanRequestDeviceIdAttestation( @@ -5757,6 +5758,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { dpms.enforceCallerCanRequestDeviceIdAttestation(dpms.getCallerIdentity(admin1)); // But not another package. + mContext.binder.callingUid = DpmMockContext.ANOTHER_UID; assertExpectException(SecurityException.class, null, () -> dpms.enforceCallerCanRequestDeviceIdAttestation( dpms.getCallerIdentity(null, admin2.getPackageName()))); diff --git a/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java index 59d4e2ae6ae4..058794a3b9e9 100644 --- a/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java @@ -16,7 +16,7 @@ package com.android.server.devicestate; -import static com.android.server.devicestate.DeviceStateManagerService.INVALID_DEVICE_STATE; +import static android.hardware.devicestate.DeviceStateManager.INVALID_DEVICE_STATE; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertThrows; diff --git a/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java b/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java index b69cc47ba738..ec747acd376d 100644 --- a/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java @@ -83,8 +83,8 @@ public class AutomaticBrightnessControllerTest { BRIGHTNESS_MAX_FLOAT, DOZE_SCALE_FACTOR, LIGHT_SENSOR_RATE, INITIAL_LIGHT_SENSOR_RATE, BRIGHTENING_LIGHT_DEBOUNCE_CONFIG, DARKENING_LIGHT_DEBOUNCE_CONFIG, RESET_AMBIENT_LUX_AFTER_WARMUP_CONFIG, - mAmbientBrightnessThresholds, mScreenBrightnessThresholds, mContext, - mDisplayDeviceConfig); + mAmbientBrightnessThresholds, mScreenBrightnessThresholds, mContext + ); controller.setLoggingEnabled(true); // Configure the brightness controller and grab an instance of the sensor listener, diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java new file mode 100644 index 000000000000..c7331e19e698 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecMessageValidatorTest.java @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.hdmi; + +import static com.android.server.hdmi.HdmiCecMessageValidator.ERROR_DESTINATION; +import static com.android.server.hdmi.HdmiCecMessageValidator.ERROR_PARAMETER_SHORT; +import static com.android.server.hdmi.HdmiCecMessageValidator.ERROR_SOURCE; +import static com.android.server.hdmi.HdmiCecMessageValidator.OK; + +import static com.google.common.truth.Truth.assertThat; + +import android.platform.test.annotations.Presubmit; + +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.SmallTest; + +import com.google.common.truth.IntegerSubject; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** Tests for {@link com.android.server.hdmi.HdmiCecMessageValidator} class. */ +@SmallTest +@Presubmit +@RunWith(JUnit4.class) +public class HdmiCecMessageValidatorTest { + + private HdmiCecMessageValidator mHdmiCecMessageValidator; + + @Before + public void setUp() throws Exception { + HdmiControlService mHdmiControlService = new HdmiControlService( + InstrumentationRegistry.getTargetContext()); + + mHdmiCecMessageValidator = new HdmiCecMessageValidator(mHdmiControlService); + } + + @Test + public void isValid_giveDevicePowerStatus() { + assertMessageValidity("04:8F").isEqualTo(OK); + + assertMessageValidity("0F:8F").isEqualTo(ERROR_DESTINATION); + assertMessageValidity("F4:8F").isEqualTo(ERROR_SOURCE); + } + + @Test + public void isValid_reportPowerStatus() { + assertMessageValidity("04:90:00").isEqualTo(OK); + + assertMessageValidity("0F:90:00").isEqualTo(ERROR_DESTINATION); + assertMessageValidity("F0:90").isEqualTo(ERROR_SOURCE); + assertMessageValidity("04:90").isEqualTo(ERROR_PARAMETER_SHORT); + } + + private IntegerSubject assertMessageValidity(String message) { + return assertThat(mHdmiCecMessageValidator.isValid(buildMessage(message))); + } + + /** + * Build a CEC message from a hex byte string with bytes separated by {@code :}. + * + * <p>This format is used by both cec-client and www.cec-o-matic.com + */ + private static HdmiCecMessage buildMessage(String message) { + String[] parts = message.split(":"); + int src = Integer.parseInt(parts[0].substring(0, 1), 16); + int dest = Integer.parseInt(parts[0].substring(1, 2), 16); + int opcode = Integer.parseInt(parts[1], 16); + byte[] params = new byte[parts.length - 2]; + for (int i = 0; i < params.length; i++) { + params[i] = (byte) Integer.parseInt(parts[i + 2], 16); + } + return new HdmiCecMessage(src, dest, opcode, params); + } +} diff --git a/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplRebootTests.java b/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplRebootTests.java index e281f2b206f5..391611b72dab 100644 --- a/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplRebootTests.java +++ b/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplRebootTests.java @@ -35,8 +35,8 @@ import java.util.function.BiConsumer; @RunWith(AndroidJUnit4.class) public class OverlayManagerServiceImplRebootTests extends OverlayManagerServiceImplTestsBase { - private static final String OVERLAY = "com.dummy.overlay"; - private static final String TARGET = "com.dummy.target"; + private static final String OVERLAY = "com.test.overlay"; + private static final String TARGET = "com.test.target"; private static final int USER = 0; private static final String OVERLAY2 = OVERLAY + "2"; diff --git a/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTests.java b/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTests.java index c1d862ab2ad4..4f882ce13dd4 100644 --- a/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTests.java +++ b/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTests.java @@ -39,8 +39,8 @@ import java.util.Map; @RunWith(AndroidJUnit4.class) public class OverlayManagerServiceImplTests extends OverlayManagerServiceImplTestsBase { - private static final String OVERLAY = "com.dummy.overlay"; - private static final String TARGET = "com.dummy.target"; + private static final String OVERLAY = "com.test.overlay"; + private static final String TARGET = "com.test.target"; private static final int USER = 0; private static final String OVERLAY2 = OVERLAY + "2"; @@ -50,7 +50,7 @@ public class OverlayManagerServiceImplTests extends OverlayManagerServiceImplTes private static final String OVERLAY3 = OVERLAY + "3"; private static final int USER3 = USER2 + 1; - private static final String CONFIG_SIGNATURE_REFERENCE_PKG = "com.dummy.ref"; + private static final String CONFIG_SIGNATURE_REFERENCE_PKG = "com.test.ref"; private static final String CERT_CONFIG_OK = "config_certificate_ok"; private static final String CERT_CONFIG_NOK = "config_certificate_nok"; @@ -149,7 +149,7 @@ public class OverlayManagerServiceImplTests extends OverlayManagerServiceImplTes installNewPackage(overlay(OVERLAY, TARGET), USER); assertState(STATE_MISSING_TARGET, OVERLAY, USER); - final DummyDeviceState.PackageBuilder target = target(TARGET); + final FakeDeviceState.PackageBuilder target = target(TARGET); installNewPackage(target, USER); assertState(STATE_DISABLED, OVERLAY, USER); @@ -169,9 +169,9 @@ public class OverlayManagerServiceImplTests extends OverlayManagerServiceImplTes @Test public void testOnOverlayPackageUpgraded() { - final DummyListener listener = getListener(); - final DummyDeviceState.PackageBuilder target = target(TARGET); - final DummyDeviceState.PackageBuilder overlay = overlay(OVERLAY, TARGET); + final FakeListener listener = getListener(); + final FakeDeviceState.PackageBuilder target = target(TARGET); + final FakeDeviceState.PackageBuilder overlay = overlay(OVERLAY, TARGET); installNewPackage(target, USER); installNewPackage(overlay, USER); listener.count = 0; @@ -181,7 +181,7 @@ public class OverlayManagerServiceImplTests extends OverlayManagerServiceImplTes // upgrade to a version where the overlay has changed its target // expect once for the old target package, once for the new target package listener.count = 0; - final DummyDeviceState.PackageBuilder overlay2 = overlay(OVERLAY, "some.other.target"); + final FakeDeviceState.PackageBuilder overlay2 = overlay(OVERLAY, "some.other.target"); upgradePackage(overlay2, USER); assertEquals(3, listener.count); @@ -193,7 +193,7 @@ public class OverlayManagerServiceImplTests extends OverlayManagerServiceImplTes @Test public void testListener() { final OverlayManagerServiceImpl impl = getImpl(); - final DummyListener listener = getListener(); + final FakeListener listener = getListener(); installNewPackage(overlay(OVERLAY, TARGET), USER); assertEquals(1, listener.count); listener.count = 0; @@ -219,12 +219,12 @@ public class OverlayManagerServiceImplTests extends OverlayManagerServiceImplTes installNewPackage(target(TARGET), USER); installNewPackage(overlay(OVERLAY, TARGET).setCertificate(CERT_CONFIG_OK), USER); - final DummyIdmapDaemon idmapd = getIdmapd(); - final DummyDeviceState state = getState(); + final FakeIdmapDaemon idmapd = getIdmapd(); + final FakeDeviceState state = getState(); String overlayPath = state.select(OVERLAY, USER).apkPath; assertTrue(idmapd.idmapExists(overlayPath, USER)); - DummyIdmapDaemon.IdmapHeader idmap = idmapd.getIdmap(overlayPath); + FakeIdmapDaemon.IdmapHeader idmap = idmapd.getIdmap(overlayPath); assertTrue((CONFIG_SIGNATURE & idmap.policies) == CONFIG_SIGNATURE); } @@ -237,12 +237,12 @@ public class OverlayManagerServiceImplTests extends OverlayManagerServiceImplTes installNewPackage(target(TARGET), USER); installNewPackage(overlay(OVERLAY, TARGET).setCertificate(CERT_CONFIG_NOK), USER); - final DummyIdmapDaemon idmapd = getIdmapd(); - final DummyDeviceState state = getState(); + final FakeIdmapDaemon idmapd = getIdmapd(); + final FakeDeviceState state = getState(); String overlayPath = state.select(OVERLAY, USER).apkPath; assertTrue(idmapd.idmapExists(overlayPath, USER)); - DummyIdmapDaemon.IdmapHeader idmap = idmapd.getIdmap(overlayPath); + FakeIdmapDaemon.IdmapHeader idmap = idmapd.getIdmap(overlayPath); assertTrue((CONFIG_SIGNATURE & idmap.policies) == 0); } @@ -252,12 +252,12 @@ public class OverlayManagerServiceImplTests extends OverlayManagerServiceImplTes installNewPackage(target(TARGET), USER); installNewPackage(overlay(OVERLAY, TARGET).setCertificate(CERT_CONFIG_NOK), USER); - final DummyIdmapDaemon idmapd = getIdmapd(); - final DummyDeviceState state = getState(); + final FakeIdmapDaemon idmapd = getIdmapd(); + final FakeDeviceState state = getState(); String overlayPath = state.select(OVERLAY, USER).apkPath; assertTrue(idmapd.idmapExists(overlayPath, USER)); - DummyIdmapDaemon.IdmapHeader idmap = idmapd.getIdmap(overlayPath); + FakeIdmapDaemon.IdmapHeader idmap = idmapd.getIdmap(overlayPath); assertTrue((CONFIG_SIGNATURE & idmap.policies) == 0); } @@ -266,12 +266,12 @@ public class OverlayManagerServiceImplTests extends OverlayManagerServiceImplTes installNewPackage(target(TARGET), USER); installNewPackage(overlay(OVERLAY, TARGET).setCertificate(CERT_CONFIG_NOK), USER); - final DummyIdmapDaemon idmapd = getIdmapd(); - final DummyDeviceState state = getState(); + final FakeIdmapDaemon idmapd = getIdmapd(); + final FakeDeviceState state = getState(); String overlayPath = state.select(OVERLAY, USER).apkPath; assertTrue(idmapd.idmapExists(overlayPath, USER)); - DummyIdmapDaemon.IdmapHeader idmap = idmapd.getIdmap(overlayPath); + FakeIdmapDaemon.IdmapHeader idmap = idmapd.getIdmap(overlayPath); assertTrue((CONFIG_SIGNATURE & idmap.policies) == 0); } @@ -284,12 +284,12 @@ public class OverlayManagerServiceImplTests extends OverlayManagerServiceImplTes installNewPackage(target(TARGET), USER); installNewPackage(overlay(OVERLAY, TARGET).setCertificate(CERT_CONFIG_NOK), USER); - final DummyIdmapDaemon idmapd = getIdmapd(); - final DummyDeviceState state = getState(); + final FakeIdmapDaemon idmapd = getIdmapd(); + final FakeDeviceState state = getState(); String overlayPath = state.select(OVERLAY, USER).apkPath; assertTrue(idmapd.idmapExists(overlayPath, USER)); - DummyIdmapDaemon.IdmapHeader idmap = idmapd.getIdmap(overlayPath); + FakeIdmapDaemon.IdmapHeader idmap = idmapd.getIdmap(overlayPath); assertTrue((CONFIG_SIGNATURE & idmap.policies) == 0); } } diff --git a/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTestsBase.java b/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTestsBase.java index 2faf29f45375..006dda0f80e3 100644 --- a/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTestsBase.java +++ b/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTestsBase.java @@ -48,19 +48,19 @@ import java.util.stream.Collectors; /** Base class for creating {@link OverlayManagerServiceImplTests} tests. */ class OverlayManagerServiceImplTestsBase { private OverlayManagerServiceImpl mImpl; - private DummyDeviceState mState; - private DummyListener mListener; - private DummyPackageManagerHelper mPackageManager; - private DummyIdmapDaemon mIdmapDaemon; + private FakeDeviceState mState; + private FakeListener mListener; + private FakePackageManagerHelper mPackageManager; + private FakeIdmapDaemon mIdmapDaemon; private OverlayConfig mOverlayConfig; private String mConfigSignaturePackageName; @Before public void setUp() { - mState = new DummyDeviceState(); - mListener = new DummyListener(); - mPackageManager = new DummyPackageManagerHelper(mState); - mIdmapDaemon = new DummyIdmapDaemon(mState); + mState = new FakeDeviceState(); + mListener = new FakeListener(); + mPackageManager = new FakePackageManagerHelper(mState); + mIdmapDaemon = new FakeIdmapDaemon(mState); mOverlayConfig = mock(OverlayConfig.class); when(mOverlayConfig.getPriority(any())).thenReturn(OverlayConfig.DEFAULT_PRIORITY); when(mOverlayConfig.isEnabled(any())).thenReturn(false); @@ -81,15 +81,15 @@ class OverlayManagerServiceImplTestsBase { return mImpl; } - DummyListener getListener() { + FakeListener getListener() { return mListener; } - DummyIdmapDaemon getIdmapd() { + FakeIdmapDaemon getIdmapd() { return mIdmapDaemon; } - DummyDeviceState getState() { + FakeDeviceState getState() { return mState; } @@ -116,27 +116,27 @@ class OverlayManagerServiceImplTestsBase { assertEquals(expected, actual); } - DummyDeviceState.PackageBuilder app(String packageName) { - return new DummyDeviceState.PackageBuilder(packageName, null /* targetPackageName */, + FakeDeviceState.PackageBuilder app(String packageName) { + return new FakeDeviceState.PackageBuilder(packageName, null /* targetPackageName */, null /* targetOverlayableName */, "data"); } - DummyDeviceState.PackageBuilder target(String packageName) { - return new DummyDeviceState.PackageBuilder(packageName, null /* targetPackageName */, + FakeDeviceState.PackageBuilder target(String packageName) { + return new FakeDeviceState.PackageBuilder(packageName, null /* targetPackageName */, null /* targetOverlayableName */, ""); } - DummyDeviceState.PackageBuilder overlay(String packageName, String targetPackageName) { + FakeDeviceState.PackageBuilder overlay(String packageName, String targetPackageName) { return overlay(packageName, targetPackageName, null /* targetOverlayableName */); } - DummyDeviceState.PackageBuilder overlay(String packageName, String targetPackageName, + FakeDeviceState.PackageBuilder overlay(String packageName, String targetPackageName, String targetOverlayableName) { - return new DummyDeviceState.PackageBuilder(packageName, targetPackageName, + return new FakeDeviceState.PackageBuilder(packageName, targetPackageName, targetOverlayableName, ""); } - void addPackage(DummyDeviceState.PackageBuilder pkg, int userId) { + void addPackage(FakeDeviceState.PackageBuilder pkg, int userId) { mState.add(pkg, userId); } @@ -155,7 +155,7 @@ class OverlayManagerServiceImplTestsBase { * * @throws IllegalStateException if the package is currently installed */ - void installNewPackage(DummyDeviceState.PackageBuilder pkg, int userId) { + void installNewPackage(FakeDeviceState.PackageBuilder pkg, int userId) { if (mState.select(pkg.packageName, userId) != null) { throw new IllegalStateException("package " + pkg.packageName + " already installed"); } @@ -178,8 +178,8 @@ class OverlayManagerServiceImplTestsBase { * * @throws IllegalStateException if the package is not currently installed */ - void upgradePackage(DummyDeviceState.PackageBuilder pkg, int userId) { - final DummyDeviceState.Package replacedPackage = mState.select(pkg.packageName, userId); + void upgradePackage(FakeDeviceState.PackageBuilder pkg, int userId) { + final FakeDeviceState.Package replacedPackage = mState.select(pkg.packageName, userId); if (replacedPackage == null) { throw new IllegalStateException("package " + pkg.packageName + " not installed"); } @@ -204,7 +204,7 @@ class OverlayManagerServiceImplTestsBase { * @throws IllegalStateException if the package is not currently installed */ void uninstallPackage(String packageName, int userId) { - final DummyDeviceState.Package pkg = mState.select(packageName, userId); + final FakeDeviceState.Package pkg = mState.select(packageName, userId); if (pkg == null) { throw new IllegalStateException("package " + packageName+ " not installed"); } @@ -217,7 +217,7 @@ class OverlayManagerServiceImplTestsBase { } /** Represents the state of packages installed on a fake device. */ - static class DummyDeviceState { + static class FakeDeviceState { private ArrayMap<String, Package> mPackages = new ArrayMap<>(); void add(PackageBuilder pkgBuilder, int userId) { @@ -333,16 +333,16 @@ class OverlayManagerServiceImplTestsBase { } } - final class DummyPackageManagerHelper implements PackageManagerHelper { - private final DummyDeviceState mState; + final class FakePackageManagerHelper implements PackageManagerHelper { + private final FakeDeviceState mState; - private DummyPackageManagerHelper(DummyDeviceState state) { + private FakePackageManagerHelper(FakeDeviceState state) { mState = state; } @Override public PackageInfo getPackageInfo(@NonNull String packageName, int userId) { - final DummyDeviceState.Package pkg = mState.select(packageName, userId); + final FakeDeviceState.Package pkg = mState.select(packageName, userId); if (pkg == null) { return null; } @@ -353,15 +353,15 @@ class OverlayManagerServiceImplTestsBase { pi.packageName = pkg.packageName; pi.overlayTarget = pkg.targetPackageName; pi.targetOverlayableName = pkg.targetOverlayableName; - pi.overlayCategory = "dummy-category-" + pkg.targetPackageName; + pi.overlayCategory = "Fake-category-" + pkg.targetPackageName; return pi; } @Override public boolean signaturesMatching(@NonNull String packageName1, @NonNull String packageName2, int userId) { - final DummyDeviceState.Package pkg1 = mState.select(packageName1, userId); - final DummyDeviceState.Package pkg2 = mState.select(packageName2, userId); + final FakeDeviceState.Package pkg1 = mState.select(packageName1, userId); + final FakeDeviceState.Package pkg2 = mState.select(packageName2, userId); return pkg1 != null && pkg2 != null && pkg1.certificate.equals(pkg2.certificate); } @@ -382,7 +382,7 @@ class OverlayManagerServiceImplTestsBase { @Override public OverlayableInfo getOverlayableForTarget(@NonNull String packageName, @NonNull String targetOverlayableName, int userId) { - final DummyDeviceState.Package pkg = mState.select(packageName, userId); + final FakeDeviceState.Package pkg = mState.select(packageName, userId); if (pkg == null || !pkg.overlayableNames.contains(targetOverlayableName)) { return null; } @@ -403,7 +403,7 @@ class OverlayManagerServiceImplTestsBase { @Override public boolean doesTargetDefineOverlayable(String targetPackageName, int userId) { - final DummyDeviceState.Package pkg = mState.select(targetPackageName, userId); + final FakeDeviceState.Package pkg = mState.select(targetPackageName, userId); return pkg != null && pkg.overlayableNames.contains(targetPackageName); } @@ -413,16 +413,16 @@ class OverlayManagerServiceImplTestsBase { } } - static class DummyIdmapDaemon extends IdmapDaemon { - private final DummyDeviceState mState; + static class FakeIdmapDaemon extends IdmapDaemon { + private final FakeDeviceState mState; private final ArrayMap<String, IdmapHeader> mIdmapFiles = new ArrayMap<>(); - DummyIdmapDaemon(DummyDeviceState state) { + FakeIdmapDaemon(FakeDeviceState state) { this.mState = state; } private int getCrc(@NonNull final String path) { - final DummyDeviceState.Package pkg = mState.selectFromPath(path); + final FakeDeviceState.Package pkg = mState.selectFromPath(path); Assert.assertNotNull(pkg); return pkg.versionCode; } @@ -486,7 +486,7 @@ class OverlayManagerServiceImplTestsBase { } } - static class DummyListener implements OverlayManagerServiceImpl.OverlayChangeListener { + static class FakeListener implements OverlayManagerServiceImpl.OverlayChangeListener { public int count; public void onOverlaysChanged(@NonNull String targetPackage, int userId) { diff --git a/services/tests/servicestests/src/com/android/server/om/OverlayManagerSettingsTests.java b/services/tests/servicestests/src/com/android/server/om/OverlayManagerSettingsTests.java index 146f60aff724..9ef755791c80 100644 --- a/services/tests/servicestests/src/com/android/server/om/OverlayManagerSettingsTests.java +++ b/services/tests/servicestests/src/com/android/server/om/OverlayManagerSettingsTests.java @@ -50,55 +50,55 @@ public class OverlayManagerSettingsTests { private OverlayManagerSettings mSettings; private static final OverlayInfo OVERLAY_A0 = new OverlayInfo( - "com.dummy.overlay_a", - "com.dummy.target", + "com.test.overlay_a", + "com.test.target", null, "some-category", - "/data/app/com.dummy.overlay_a-1/base.apk", + "/data/app/com.test.overlay_a-1/base.apk", STATE_DISABLED, 0, 0, true); private static final OverlayInfo OVERLAY_B0 = new OverlayInfo( - "com.dummy.overlay_b", - "com.dummy.target", + "com.test.overlay_b", + "com.test.target", null, "some-category", - "/data/app/com.dummy.overlay_b-1/base.apk", + "/data/app/com.test.overlay_b-1/base.apk", STATE_DISABLED, 0, 0, true); private static final OverlayInfo OVERLAY_C0 = new OverlayInfo( - "com.dummy.overlay_c", - "com.dummy.target", + "com.test.overlay_c", + "com.test.target", null, "some-category", - "/data/app/com.dummy.overlay_c-1/base.apk", + "/data/app/com.test.overlay_c-1/base.apk", STATE_DISABLED, 0, 0, true); private static final OverlayInfo OVERLAY_A1 = new OverlayInfo( - "com.dummy.overlay_a", - "com.dummy.target", + "com.test.overlay_a", + "com.test.target", null, "some-category", - "/data/app/com.dummy.overlay_a-1/base.apk", + "/data/app/com.test.overlay_a-1/base.apk", STATE_DISABLED, 1, 0, true); private static final OverlayInfo OVERLAY_B1 = new OverlayInfo( - "com.dummy.overlay_b", - "com.dummy.target", + "com.test.overlay_b", + "com.test.target", null, "some-category", - "/data/app/com.dummy.overlay_b-1/base.apk", + "/data/app/com.test.overlay_b-1/base.apk", STATE_DISABLED, 1, 0, @@ -230,11 +230,11 @@ public class OverlayManagerSettingsTests { assertListsAreEqual(list, OVERLAY_A0, OVERLAY_C0, OVERLAY_B0); OverlayInfo otherTarget = new OverlayInfo( - "com.dummy.overlay_other", - "com.dummy.some.other.target", + "com.test.overlay_other", + "com.test.some.other.target", null, "some-category", - "/data/app/com.dummy.overlay_other-1/base.apk", + "/data/app/com.test.overlay_other-1/base.apk", STATE_DISABLED, 0, 0, @@ -350,7 +350,7 @@ public class OverlayManagerSettingsTests { ByteArrayInputStream is = new ByteArrayInputStream(xml.getBytes("utf-8")); mSettings.restore(is); - assertDoesNotContain(mSettings, "com.dummy.overlay", 0); + assertDoesNotContain(mSettings, "com.test.overlay", 0); } @Test @@ -359,27 +359,27 @@ public class OverlayManagerSettingsTests { final String xml = "<?xml version='1.0' encoding='utf-8' standalone='yes'?>\n" + "<overlays version='" + version + "'>\n" - + "<item packageName='com.dummy.overlay'\n" + + "<item packageName='com.test.overlay'\n" + " userId='1234'\n" - + " targetPackageName='com.dummy.target'\n" - + " baseCodePath='/data/app/com.dummy.overlay-1/base.apk'\n" + + " targetPackageName='com.test.target'\n" + + " baseCodePath='/data/app/com.test.overlay-1/base.apk'\n" + " state='" + STATE_DISABLED + "'\n" + " isEnabled='false'\n" - + " category='dummy-category'\n" + + " category='test-category'\n" + " isStatic='false'\n" + " priority='0' />\n" + "</overlays>\n"; ByteArrayInputStream is = new ByteArrayInputStream(xml.getBytes("utf-8")); mSettings.restore(is); - OverlayInfo oi = mSettings.getOverlayInfo("com.dummy.overlay", 1234); + OverlayInfo oi = mSettings.getOverlayInfo("com.test.overlay", 1234); assertNotNull(oi); - assertEquals("com.dummy.overlay", oi.packageName); - assertEquals("com.dummy.target", oi.targetPackageName); - assertEquals("/data/app/com.dummy.overlay-1/base.apk", oi.baseCodePath); + assertEquals("com.test.overlay", oi.packageName); + assertEquals("com.test.target", oi.targetPackageName); + assertEquals("/data/app/com.test.overlay-1/base.apk", oi.baseCodePath); assertEquals(1234, oi.userId); assertEquals(STATE_DISABLED, oi.state); - assertFalse(mSettings.getEnabled("com.dummy.overlay", 1234)); + assertFalse(mSettings.getEnabled("com.test.overlay", 1234)); } @Test diff --git a/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java b/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java index f37054d269b1..9ce4ee0e8fac 100644 --- a/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java @@ -41,6 +41,7 @@ import static org.mockito.Mockito.when; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.UserIdInt; import android.app.Notification; import android.app.NotificationChannel; import android.app.NotificationManager; @@ -100,6 +101,7 @@ import java.util.concurrent.Executor; import java.util.concurrent.ScheduledExecutorService; import java.util.function.BiConsumer; import java.util.function.Consumer; +import java.util.function.Function; @RunWith(JUnit4.class) public final class DataManagerTest { @@ -660,6 +662,26 @@ public final class DataManagerTest { } @Test + public void testConversationLastEventTimestampUpdate() { + mDataManager.onUserUnlocked(USER_ID_PRIMARY); + + ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID, + buildPerson()); + mDataManager.addOrUpdateConversationInfo(shortcut); + + PackageData packageData = mDataManager.getPackage(TEST_PKG_NAME, USER_ID_PRIMARY); + ConversationInfo conversationInfo = + packageData.getConversationStore().getConversation(TEST_SHORTCUT_ID); + Event event = new Event(123L, Event.TYPE_IN_APP_CONVERSATION); + + mInjector.mUsageStatsQueryHelper.mEventListener.onEvent(packageData, conversationInfo, + event); + ConversationInfo newConversationInfo = + packageData.getConversationStore().getConversation(TEST_SHORTCUT_ID); + assertEquals(123L, newConversationInfo.getLastEventTimestamp()); + } + + @Test public void testDeleteUninstalledPackageDataOnPackageRemoved() { mDataManager.onUserUnlocked(USER_ID_PRIMARY); @@ -1122,6 +1144,18 @@ public final class DataManagerTest { } } + private class TestUsageStatsQueryHelper extends UsageStatsQueryHelper { + + private final EventListener mEventListener; + + TestUsageStatsQueryHelper(int userId, + Function<String, PackageData> packageDataGetter, + EventListener eventListener) { + super(userId, packageDataGetter, eventListener); + mEventListener = eventListener; + } + } + private class TestInjector extends DataManager.Injector { private final TestContactsQueryHelper mContactsQueryHelper = @@ -1129,6 +1163,7 @@ public final class DataManagerTest { private TestCallLogQueryHelper mCallLogQueryHelper; private TestMmsQueryHelper mMmsQueryHelper; private TestSmsQueryHelper mSmsQueryHelper; + private TestUsageStatsQueryHelper mUsageStatsQueryHelper; @Override ScheduledExecutorService createScheduledExecutor() { @@ -1165,5 +1200,14 @@ public final class DataManagerTest { mSmsQueryHelper = new TestSmsQueryHelper(context, eventConsumer); return mSmsQueryHelper; } + + @Override + UsageStatsQueryHelper createUsageStatsQueryHelper(@UserIdInt int userId, + Function<String, PackageData> packageDataGetter, + UsageStatsQueryHelper.EventListener eventListener) { + mUsageStatsQueryHelper = + new TestUsageStatsQueryHelper(userId, packageDataGetter, eventListener); + return mUsageStatsQueryHelper; + } } } diff --git a/services/tests/servicestests/src/com/android/server/people/data/UsageStatsQueryHelperTest.java b/services/tests/servicestests/src/com/android/server/people/data/UsageStatsQueryHelperTest.java index e9686071409e..baa74b7ce960 100644 --- a/services/tests/servicestests/src/com/android/server/people/data/UsageStatsQueryHelperTest.java +++ b/services/tests/servicestests/src/com/android/server/people/data/UsageStatsQueryHelperTest.java @@ -26,6 +26,7 @@ import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.annotation.NonNull; @@ -72,6 +73,8 @@ public final class UsageStatsQueryHelperTest { @Mock private UsageStatsManagerInternal mUsageStatsManagerInternal; + @Mock + private UsageStatsQueryHelper.EventListener mEventListener; private TestPackageData mPackageData; private UsageStatsQueryHelper mHelper; @@ -93,7 +96,7 @@ public final class UsageStatsQueryHelperTest { .setLocusId(LOCUS_ID_1) .build(); - mHelper = new UsageStatsQueryHelper(USER_ID_PRIMARY, pkg -> mPackageData); + mHelper = new UsageStatsQueryHelper(USER_ID_PRIMARY, pkg -> mPackageData, mEventListener); } @After @@ -131,6 +134,8 @@ public final class UsageStatsQueryHelperTest { Event.ALL_EVENT_TYPES, 0L, Long.MAX_VALUE); assertEquals(1, events.size()); assertEquals(createInAppConversationEvent(100_000L, 10), events.get(0)); + verify(mEventListener).onEvent( + mPackageData, mPackageData.mConversationStore.mConversationInfo, events.get(0)); } @Test @@ -145,6 +150,8 @@ public final class UsageStatsQueryHelperTest { Event.ALL_EVENT_TYPES, 0L, Long.MAX_VALUE); assertEquals(1, events.size()); assertEquals(createInAppConversationEvent(100_000L, 10), events.get(0)); + verify(mEventListener).onEvent( + mPackageData, mPackageData.mConversationStore.mConversationInfo, events.get(0)); } @Test @@ -159,6 +166,8 @@ public final class UsageStatsQueryHelperTest { Event.ALL_EVENT_TYPES, 0L, Long.MAX_VALUE); assertEquals(1, events.size()); assertEquals(createInAppConversationEvent(100_000L, 10), events.get(0)); + verify(mEventListener).onEvent( + mPackageData, mPackageData.mConversationStore.mConversationInfo, events.get(0)); } @Test diff --git a/services/tests/servicestests/src/com/android/server/pm/IncrementalStatesTest.java b/services/tests/servicestests/src/com/android/server/pm/IncrementalStatesTest.java index 62e135b09593..86758f18a407 100644 --- a/services/tests/servicestests/src/com/android/server/pm/IncrementalStatesTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/IncrementalStatesTest.java @@ -20,7 +20,6 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; -import android.content.pm.IDataLoaderStatusListener; import android.content.pm.PackageManager; import android.os.ConditionVariable; import android.os.incremental.IStorageHealthListener; @@ -113,96 +112,30 @@ public class IncrementalStatesTest { } /** - * Test that the package is still startable when Incremental Storage is at blocked status. + * Test that the package becomes unstartable when health status indicate storage issues. */ @Test public void testStartableTransition_IncrementalStorageBlocked() { mIncrementalStates.onStorageHealthStatusChanged( - IStorageHealthListener.HEALTH_STATUS_BLOCKED); - // Test that package is still startable - assertFalse(mUnstartableCalled.block(WAIT_TIMEOUT_MILLIS)); - assertTrue(mIncrementalStates.isStartable()); - } - - /** - * Test that the package is still startable when Data Loader has unknown transportation issues. - */ - @Test - public void testStartableTransition_DataLoaderTransportError() { - mIncrementalStates.onStreamStatusChanged( - IDataLoaderStatusListener.STREAM_TRANSPORT_ERROR); - // Test that package is still startable - assertFalse(mUnstartableCalled.block(WAIT_TIMEOUT_MILLIS)); - assertTrue(mIncrementalStates.isStartable()); - } - - /** - * Test that the package becomes unstartable when Data Loader has data integrity issues. - */ - @Test - public void testStartableTransition_DataLoaderIntegrityError() { - mIncrementalStates.onStreamStatusChanged( - IDataLoaderStatusListener.STREAM_INTEGRITY_ERROR); - // Test that package is now unstartable - assertTrue(mUnstartableCalled.block(WAIT_TIMEOUT_MILLIS)); - assertFalse(mIncrementalStates.isStartable()); - assertEquals(PackageManager.UNSTARTABLE_REASON_DATALOADER_TRANSPORT, - mUnstartableReason.get()); - } - - /** - * Test that the package becomes unstartable when Data Loader has data source issues. - */ - @Test - public void testStartableTransition_DataLoaderSourceError() { - mIncrementalStates.onStreamStatusChanged( - IDataLoaderStatusListener.STREAM_SOURCE_ERROR); + IStorageHealthListener.HEALTH_STATUS_UNHEALTHY_STORAGE); // Test that package is now unstartable assertTrue(mUnstartableCalled.block(WAIT_TIMEOUT_MILLIS)); assertFalse(mIncrementalStates.isStartable()); - assertEquals(PackageManager.UNSTARTABLE_REASON_DATALOADER_TRANSPORT, + assertEquals(PackageManager.UNSTARTABLE_REASON_INSUFFICIENT_STORAGE, mUnstartableReason.get()); } /** - * Test that the package becomes unstartable when Data Loader hits limited storage while - * Incremental storage has a pending reads. + * Test that the package becomes unstartable when health status indicates transport issues. */ @Test - public void testStartableTransition_DataLoaderStorageErrorWhenIncrementalStoragePending() - throws InterruptedException { - mIncrementalStates.onStreamStatusChanged( - IDataLoaderStatusListener.STREAM_STORAGE_ERROR); - // Test that package is still startable - assertFalse(mUnstartableCalled.block(WAIT_TIMEOUT_MILLIS)); - assertTrue(mIncrementalStates.isStartable()); - mIncrementalStates.onStorageHealthStatusChanged( - IStorageHealthListener.HEALTH_STATUS_READS_PENDING); - // Test that package is now unstartable - assertTrue(mUnstartableCalled.block(WAIT_TIMEOUT_MILLIS)); - assertFalse(mIncrementalStates.isStartable()); - assertEquals(PackageManager.UNSTARTABLE_REASON_DATALOADER_STORAGE, - mUnstartableReason.get()); - } - - /** - * Test that the package becomes unstartable when Data Loader hits limited storage while - * Incremental storage is at blocked status. - */ - @Test - public void testStartableTransition_DataLoaderStorageErrorWhenIncrementalStorageBlocked() - throws InterruptedException { - mIncrementalStates.onStreamStatusChanged( - IDataLoaderStatusListener.STREAM_STORAGE_ERROR); - // Test that package is still startable - assertFalse(mUnstartableCalled.block(WAIT_TIMEOUT_MILLIS)); - assertTrue(mIncrementalStates.isStartable()); + public void testStartableTransition_DataLoaderIntegrityError() { mIncrementalStates.onStorageHealthStatusChanged( - IStorageHealthListener.HEALTH_STATUS_BLOCKED); + IStorageHealthListener.HEALTH_STATUS_UNHEALTHY_TRANSPORT); // Test that package is now unstartable assertTrue(mUnstartableCalled.block(WAIT_TIMEOUT_MILLIS)); assertFalse(mIncrementalStates.isStartable()); - assertEquals(PackageManager.UNSTARTABLE_REASON_DATALOADER_STORAGE, + assertEquals(PackageManager.UNSTARTABLE_REASON_CONNECTION_ERROR, mUnstartableReason.get()); } @@ -227,42 +160,18 @@ public class IncrementalStatesTest { } /** - * Test that the package becomes unstartable when Data Loader has data integrity issue, and it - * becomes startable again when Data Loader is healthy again. + * Test that the package becomes unstartable when health status indicates transportation issue, + * and it becomes startable again when health status is ok again. */ @Test public void testStartableTransition_DataLoaderUnhealthyBackToHealthy() throws InterruptedException { - mIncrementalStates.onStreamStatusChanged(IDataLoaderStatusListener.STREAM_INTEGRITY_ERROR); - // Test that package is unstartable - assertTrue(mUnstartableCalled.block(WAIT_TIMEOUT_MILLIS)); - assertFalse(mIncrementalStates.isStartable()); - - mIncrementalStates.onStreamStatusChanged(IDataLoaderStatusListener.STREAM_HEALTHY); - // Test that package is now startable - assertTrue(mStartableCalled.block(WAIT_TIMEOUT_MILLIS)); - assertTrue(mIncrementalStates.isStartable()); - } - - /** - * Test that the package becomes unstartable when both Incremental Storage and Data Loader - * are unhealthy, and it becomes startable again when both Incremental Storage and Data Loader - * are healthy again. - */ - @Test - public void testStartableTransition_DataLoaderAndIncrementalStorageUnhealthyBackToHealthy() - throws InterruptedException { mIncrementalStates.onStorageHealthStatusChanged( - IStorageHealthListener.HEALTH_STATUS_UNHEALTHY); - mIncrementalStates.onStreamStatusChanged(IDataLoaderStatusListener.STREAM_INTEGRITY_ERROR); + IStorageHealthListener.HEALTH_STATUS_UNHEALTHY_TRANSPORT); // Test that package is unstartable assertTrue(mUnstartableCalled.block(WAIT_TIMEOUT_MILLIS)); assertFalse(mIncrementalStates.isStartable()); - mIncrementalStates.onStreamStatusChanged(IDataLoaderStatusListener.STREAM_HEALTHY); - // Test that package is still unstartable - assertFalse(mStartableCalled.block(WAIT_TIMEOUT_MILLIS)); - assertFalse(mIncrementalStates.isStartable()); mIncrementalStates.onStorageHealthStatusChanged(IStorageHealthListener.HEALTH_STATUS_OK); // Test that package is now startable assertTrue(mStartableCalled.block(WAIT_TIMEOUT_MILLIS)); diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java index 22b07157e94e..b190339b129b 100644 --- a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java @@ -41,6 +41,7 @@ import android.test.suitebuilder.annotation.MediumTest; import android.test.suitebuilder.annotation.SmallTest; import android.util.Slog; +import androidx.annotation.Nullable; import androidx.test.InstrumentationRegistry; import androidx.test.runner.AndroidJUnit4; @@ -58,6 +59,8 @@ import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; +import javax.annotation.concurrent.GuardedBy; + /** Test {@link UserManager} functionality. */ @RunWith(AndroidJUnit4.class) public final class UserManagerTest { @@ -134,7 +137,7 @@ public final class UserManagerTest { @SmallTest @Test public void testHasSystemUser() throws Exception { - assertThat(findUser(UserHandle.USER_SYSTEM)).isTrue(); + assertThat(hasUser(UserHandle.USER_SYSTEM)).isTrue(); } @MediumTest @@ -164,9 +167,9 @@ public final class UserManagerTest { assertThat(user1).isNotNull(); assertThat(user2).isNotNull(); - assertThat(findUser(UserHandle.USER_SYSTEM)).isTrue(); - assertThat(findUser(user1.id)).isTrue(); - assertThat(findUser(user2.id)).isTrue(); + assertThat(hasUser(UserHandle.USER_SYSTEM)).isTrue(); + assertThat(hasUser(user1.id)).isTrue(); + assertThat(hasUser(user2.id)).isTrue(); } @MediumTest @@ -175,7 +178,7 @@ public final class UserManagerTest { UserInfo userInfo = createUser("Guest 1", UserInfo.FLAG_GUEST); removeUser(userInfo.id); - assertThat(findUser(userInfo.id)).isFalse(); + assertThat(hasUser(userInfo.id)).isFalse(); } @MediumTest @@ -199,7 +202,7 @@ public final class UserManagerTest { } } - assertThat(findUser(userInfo.id)).isFalse(); + assertThat(hasUser(userInfo.id)).isFalse(); } @MediumTest @@ -208,6 +211,79 @@ public final class UserManagerTest { assertThrows(IllegalArgumentException.class, () -> mUserManager.removeUser(null)); } + @MediumTest + @Test + public void testRemoveUserOrSetEphemeral_restrictedReturnsError() throws Exception { + final int currentUser = ActivityManager.getCurrentUser(); + final UserInfo user1 = createUser("User 1", /* flags= */ 0); + mUserManager.setUserRestriction(UserManager.DISALLOW_REMOVE_USER, /* value= */ true, + asHandle(currentUser)); + try { + assertThat(mUserManager.removeUserOrSetEphemeral(user1.id)).isEqualTo( + UserManager.REMOVE_RESULT_ERROR); + } finally { + mUserManager.setUserRestriction(UserManager.DISALLOW_REMOVE_USER, /* value= */ false, + asHandle(currentUser)); + } + + assertThat(hasUser(user1.id)).isTrue(); + assertThat(getUser(user1.id).isEphemeral()).isFalse(); + } + + @MediumTest + @Test + public void testRemoveUserOrSetEphemeral_systemUserReturnsError() throws Exception { + assertThat(mUserManager.removeUserOrSetEphemeral(UserHandle.USER_SYSTEM)).isEqualTo( + UserManager.REMOVE_RESULT_ERROR); + + assertThat(hasUser(UserHandle.USER_SYSTEM)).isTrue(); + } + + @MediumTest + @Test + public void testRemoveUserOrSetEphemeral_invalidUserReturnsError() throws Exception { + assertThat(hasUser(Integer.MAX_VALUE)).isFalse(); + assertThat(mUserManager.removeUserOrSetEphemeral(Integer.MAX_VALUE)).isEqualTo( + UserManager.REMOVE_RESULT_ERROR); + } + + @MediumTest + @Test + public void testRemoveUserOrSetEphemeral_currentUserSetEphemeral() throws Exception { + final int startUser = ActivityManager.getCurrentUser(); + final UserInfo user1 = createUser("User 1", /* flags= */ 0); + // Switch to the user just created. + switchUser(user1.id, null, /* ignoreHandle= */ true); + + assertThat(mUserManager.removeUserOrSetEphemeral(user1.id)).isEqualTo( + UserManager.REMOVE_RESULT_SET_EPHEMERAL); + + assertThat(hasUser(user1.id)).isTrue(); + assertThat(getUser(user1.id).isEphemeral()).isTrue(); + + // Switch back to the starting user. + switchUser(startUser, null, /* ignoreHandle= */ true); + + // User is removed once switch is complete + synchronized (mUserRemoveLock) { + waitForUserRemovalLocked(user1.id); + } + assertThat(hasUser(user1.id)).isFalse(); + } + + @MediumTest + @Test + public void testRemoveUserOrSetEphemeral_nonCurrentUserRemoved() throws Exception { + final UserInfo user1 = createUser("User 1", /* flags= */ 0); + synchronized (mUserRemoveLock) { + assertThat(mUserManager.removeUserOrSetEphemeral(user1.id)).isEqualTo( + UserManager.REMOVE_RESULT_REMOVED); + waitForUserRemovalLocked(user1.id); + } + + assertThat(hasUser(user1.id)).isFalse(); + } + /** Tests creating a FULL user via specifying userType. */ @MediumTest @Test @@ -608,15 +684,20 @@ public final class UserManagerTest { () -> mUserManager.getUserCreationTime(asHandle(user.id))); } - private boolean findUser(int id) { + @Nullable + private UserInfo getUser(int id) { List<UserInfo> list = mUserManager.getUsers(); for (UserInfo user : list) { if (user.id == id) { - return true; + return user; } } - return false; + return null; + } + + private boolean hasUser(int id) { + return getUser(id) != null; } @MediumTest @@ -918,17 +999,22 @@ public final class UserManagerTest { private void removeUser(int userId) { synchronized (mUserRemoveLock) { mUserManager.removeUser(userId); - long time = System.currentTimeMillis(); - while (mUserManager.getUserInfo(userId) != null) { - try { - mUserRemoveLock.wait(REMOVE_CHECK_INTERVAL_MILLIS); - } catch (InterruptedException ie) { - Thread.currentThread().interrupt(); - return; - } - if (System.currentTimeMillis() - time > REMOVE_TIMEOUT_MILLIS) { - fail("Timeout waiting for removeUser. userId = " + userId); - } + waitForUserRemovalLocked(userId); + } + } + + @GuardedBy("mUserRemoveLock") + private void waitForUserRemovalLocked(int userId) { + long time = System.currentTimeMillis(); + while (mUserManager.getUserInfo(userId) != null) { + try { + mUserRemoveLock.wait(REMOVE_CHECK_INTERVAL_MILLIS); + } catch (InterruptedException ie) { + Thread.currentThread().interrupt(); + return; + } + if (System.currentTimeMillis() - time > REMOVE_TIMEOUT_MILLIS) { + fail("Timeout waiting for removeUser. userId = " + userId); } } } diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java index 6ee583164284..1100496dffbe 100755 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -1883,7 +1883,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { public void testGroupInstanceIds() throws Exception { final NotificationRecord group1 = generateNotificationRecord( mTestNotificationChannel, 1, "group1", true); - mBinderService.enqueueNotificationWithTag(PKG, PKG, "testFindGroupNotificationsLocked", + mBinderService.enqueueNotificationWithTag(PKG, PKG, "testGroupInstanceIds", group1.getSbn().getId(), group1.getSbn().getNotification(), group1.getSbn().getUserId()); waitForIdle(); @@ -1891,7 +1891,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { // same group, child, should be returned final NotificationRecord group1Child = generateNotificationRecord( mTestNotificationChannel, 2, "group1", false); - mBinderService.enqueueNotificationWithTag(PKG, PKG, "testFindGroupNotificationsLocked", + mBinderService.enqueueNotificationWithTag(PKG, PKG, "testGroupInstanceIds", group1Child.getSbn().getId(), group1Child.getSbn().getNotification(), group1Child.getSbn().getUserId()); waitForIdle(); diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java index dd85484605d1..ad9692f404e9 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java @@ -1523,7 +1523,8 @@ public class ActivityRecordTests extends WindowTestsBase { // Return error to skip unnecessary operation. doReturn(WindowManagerGlobal.ADD_STARTING_NOT_NEEDED).when(session).addToDisplay( any() /* window */, any() /* attrs */, - anyInt() /* viewVisibility */, anyInt() /* displayId */, any() /* outFrame */, + anyInt() /* viewVisibility */, anyInt() /* displayId */, + any() /* requestedVisibility */, any() /* outFrame */, any() /* outContentInsets */, any() /* outStableInsets */, any() /* outDisplayCutout */, any() /* outInputChannel */, any() /* outInsetsState */, any() /* outActiveControls */); diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java index caf8a720e26c..3dc258c51954 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java @@ -70,6 +70,8 @@ import android.content.pm.ActivityInfo; import android.os.Binder; import android.os.UserHandle; import android.platform.test.annotations.Presubmit; +import android.view.SurfaceControl; +import android.window.ITaskOrganizer; import androidx.test.filters.SmallTest; @@ -240,6 +242,24 @@ public class ActivityStackTests extends WindowTestsBase { @Test public void testRemoveOrganizedTask_UpdateStackReference() { + ITaskOrganizer listener = new ITaskOrganizer.Stub() { + @Override + public void onTaskAppeared( + ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) { } + + @Override + public void onTaskVanished(ActivityManager.RunningTaskInfo container) { } + + @Override + public void onTaskInfoChanged(ActivityManager.RunningTaskInfo info) { + } + + @Override + public void onBackPressedOnTaskRoot(ActivityManager.RunningTaskInfo taskInfo) { + } + }; + mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer(listener); + final Task rootHomeTask = mDefaultTaskDisplayArea.getRootHomeTask(); final ActivityRecord homeActivity = new ActivityBuilder(mAtm) .setStack(rootHomeTask) @@ -247,7 +267,7 @@ public class ActivityStackTests extends WindowTestsBase { .build(); final Task secondaryStack = (Task) WindowContainer.fromBinder( mAtm.mTaskOrganizerController.createRootTask(rootHomeTask.getDisplayId(), - WINDOWING_MODE_SPLIT_SCREEN_SECONDARY).token.asBinder()); + WINDOWING_MODE_SPLIT_SCREEN_SECONDARY).getTaskInfo().token.asBinder()); rootHomeTask.reparent(secondaryStack, POSITION_TOP); assertEquals(secondaryStack, rootHomeTask.getParent()); diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java index 3349c6dda87f..8292420c9e0a 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java @@ -23,20 +23,24 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSess import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.doCallRealMethod; import static org.mockito.Mockito.times; import static org.mockito.Mockito.when; import android.app.Activity; +import android.app.ActivityManager; import android.app.PictureInPictureParams; import android.app.servertransaction.ClientTransaction; import android.app.servertransaction.EnterPipRequestedItem; import android.content.res.Configuration; import android.graphics.Rect; import android.os.IBinder; +import android.os.PowerManager; import android.os.RemoteException; import android.platform.test.annotations.Presubmit; import android.view.IDisplayWindowListener; @@ -252,5 +256,36 @@ public class ActivityTaskManagerServiceTests extends WindowTestsBase { assertEquals(Task.ActivityState.RESUMED, homeActivity.getState()); assertEquals(homeActivity.app, mAtm.mInternal.getTopApp()); } + + @Test + public void testUpdateSleep() { + doCallRealMethod().when(mWm.mRoot).hasAwakeDisplay(); + mSupervisor.mGoingToSleepWakeLock = mock(PowerManager.WakeLock.class); + final ActivityRecord topActivity = new ActivityBuilder(mAtm).setCreateTask(true).build(); + topActivity.setState(Task.ActivityState.RESUMED, "test"); + + final Runnable assertTopNonSleeping = () -> { + assertFalse(mAtm.mInternal.isSleeping()); + assertEquals(ActivityManager.PROCESS_STATE_TOP, mAtm.mInternal.getTopProcessState()); + assertEquals(topActivity.app, mAtm.mInternal.getTopApp()); + }; + assertTopNonSleeping.run(); + + // Sleep all displays. + mWm.mRoot.forAllDisplays(display -> doReturn(true).when(display).shouldSleep()); + mAtm.updateSleepIfNeededLocked(); + + assertEquals(Task.ActivityState.PAUSING, topActivity.getState()); + assertTrue(mAtm.mInternal.isSleeping()); + assertEquals(ActivityManager.PROCESS_STATE_TOP_SLEEPING, + mAtm.mInternal.getTopProcessState()); + assertNull(mAtm.mInternal.getTopApp()); + + // Wake all displays. + mWm.mRoot.forAllDisplays(display -> doReturn(false).when(display).shouldSleep()); + mAtm.updateSleepIfNeededLocked(); + + assertTopNonSleeping.run(); + } } diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java index f88530c1f23e..01c1f1f73ee9 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java @@ -438,6 +438,28 @@ public class DisplayAreaTest { area.getOrientation(ActivityInfo.SCREEN_ORIENTATION_NOSENSOR)); } + @Test + public void testSetIgnoreOrientationRequest() { + final DisplayArea.Tokens area = new DisplayArea.Tokens(mWms, ABOVE_TASKS, "test"); + final WindowToken token = createWindowToken(TYPE_APPLICATION_OVERLAY); + spyOn(token); + doReturn(mock(DisplayContent.class)).when(token).getDisplayContent(); + doNothing().when(token).setParent(any()); + final WindowState win = createWindowState(token); + spyOn(win); + doNothing().when(win).setParent(any()); + win.mAttrs.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; + token.addChild(win, 0); + area.addChild(token); + doReturn(true).when(win).isVisible(); + + assertEquals(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE, area.getOrientation()); + + area.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */); + + assertEquals(ActivityInfo.SCREEN_ORIENTATION_UNSET, area.getOrientation()); + } + private static class TestDisplayArea<T extends WindowContainer> extends DisplayArea<T> { private TestDisplayArea(WindowManagerService wms, Rect bounds) { super(wms, ANY, "half display area"); diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java index 2920c1da3f24..5a14a249e78f 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java @@ -522,7 +522,9 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { mWindow.mAttrs.setFitInsetsTypes(0 /* types */); mDisplayContent.getInsetsPolicy().getInsetsForDispatch(mWindow) .getSource(InsetsState.ITYPE_STATUS_BAR).setVisible(false); - mWindow.getRequestedInsetsState().getSource(ITYPE_STATUS_BAR).setVisible(false); + final InsetsState requestedState = new InsetsState(); + requestedState.getSource(ITYPE_STATUS_BAR).setVisible(false); + mWindow.updateRequestedVisibility(requestedState); addWindow(mWindow); mDisplayPolicy.beginLayoutLw(mFrames, 0 /* UI mode */); @@ -544,7 +546,9 @@ public class DisplayPolicyLayoutTests extends DisplayPolicyTestsBase { mWindow.mAttrs.setFitInsetsTypes(0 /* types */); mDisplayContent.getInsetsPolicy().getInsetsForDispatch(mWindow) .getSource(InsetsState.ITYPE_STATUS_BAR).setVisible(false); - mWindow.getRequestedInsetsState().getSource(ITYPE_STATUS_BAR).setVisible(false); + final InsetsState requestedState = new InsetsState(); + requestedState.getSource(ITYPE_STATUS_BAR).setVisible(false); + mWindow.updateRequestedVisibility(requestedState); mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; addWindow(mWindow); diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java index a55423a7baf6..21bdc9e7785e 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java @@ -313,8 +313,8 @@ public class DisplayPolicyTests extends WindowTestsBase { // App requests to hide navigation bar. final InsetsState requestedState = new InsetsState(); requestedState.getSource(ITYPE_NAVIGATION_BAR).setVisible(false); - mAppWindow.updateRequestedInsetsState(requestedState); - insetsPolicy.onInsetsModified(mAppWindow, requestedState); + mAppWindow.updateRequestedVisibility(requestedState); + insetsPolicy.onInsetsModified(mAppWindow); assertNotNull(displayPolicy.mInputConsumer); // App still requests to hide navigation bar, but without BEHAVIOR_SHOW_BARS_BY_TOUCH. diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java index 8c02faf7ac09..d67120f53917 100644 --- a/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java @@ -38,7 +38,6 @@ import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.Mockito.clearInvocations; -import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; @@ -180,7 +179,7 @@ public class InsetsPolicyTest extends WindowTestsBase { final WindowState fullscreenApp = addWindow(TYPE_APPLICATION, "fullscreenApp"); final InsetsState requestedState = new InsetsState(); requestedState.getSource(ITYPE_STATUS_BAR).setVisible(false); - fullscreenApp.updateRequestedInsetsState(requestedState); + fullscreenApp.updateRequestedVisibility(requestedState); // Add a non-fullscreen dialog window. final WindowState dialog = addWindow(TYPE_APPLICATION, "dialog"); @@ -215,7 +214,7 @@ public class InsetsPolicyTest extends WindowTestsBase { final WindowState newFocusedFullscreenApp = addWindow(TYPE_APPLICATION, "newFullscreenApp"); final InsetsState newRequestedState = new InsetsState(); newRequestedState.getSource(ITYPE_STATUS_BAR).setVisible(true); - newFocusedFullscreenApp.updateRequestedInsetsState(newRequestedState); + newFocusedFullscreenApp.updateRequestedVisibility(newRequestedState); // Make sure status bar is hidden by previous insets state. mDisplayContent.getInsetsPolicy().updateBarControlTarget(fullscreenApp); @@ -232,20 +231,28 @@ public class InsetsPolicyTest extends WindowTestsBase { @UseTestDisplay(addWindows = W_ACTIVITY) @Test public void testShowTransientBars_bothCanBeTransient_appGetsBothFakeControls() { - addNonFocusableWindow(TYPE_STATUS_BAR, "statusBar") - .getControllableInsetProvider().getSource().setVisible(false); - addNonFocusableWindow(TYPE_NAVIGATION_BAR, "navBar") - .getControllableInsetProvider().getSource().setVisible(false); + final WindowState statusBar = addNonFocusableWindow(TYPE_STATUS_BAR, "statusBar"); + statusBar.setHasSurface(true); + statusBar.getControllableInsetProvider().setServerVisible(true); + final WindowState navBar = addNonFocusableWindow(TYPE_NAVIGATION_BAR, "navBar"); + navBar.setHasSurface(true); + navBar.getControllableInsetProvider().setServerVisible(true); final InsetsPolicy policy = spy(mDisplayContent.getInsetsPolicy()); + doNothing().when(policy).startAnimation(anyBoolean(), any()); - doAnswer(invocation -> { - ((InsetsState) invocation.getArgument(2)).setSourceVisible(ITYPE_STATUS_BAR, true); - ((InsetsState) invocation.getArgument(2)).setSourceVisible(ITYPE_NAVIGATION_BAR, true); - return null; - }).when(policy).startAnimation(anyBoolean(), any(), any()); - + // Make both system bars invisible. + final InsetsState requestedState = new InsetsState(); + requestedState.getSource(ITYPE_STATUS_BAR).setVisible(false); + requestedState.getSource(ITYPE_NAVIGATION_BAR).setVisible(false); + mAppWindow.updateRequestedVisibility(requestedState); policy.updateBarControlTarget(mAppWindow); + waitUntilWindowAnimatorIdle(); + assertFalse(mDisplayContent.getInsetsStateController().getRawInsetsState() + .getSource(ITYPE_STATUS_BAR).isVisible()); + assertFalse(mDisplayContent.getInsetsStateController().getRawInsetsState() + .getSource(ITYPE_NAVIGATION_BAR).isVisible()); + policy.showTransient(new int[]{ITYPE_STATUS_BAR, ITYPE_NAVIGATION_BAR}); waitUntilWindowAnimatorIdle(); final InsetsSourceControl[] controls = @@ -272,7 +279,7 @@ public class InsetsPolicyTest extends WindowTestsBase { .getControllableInsetProvider().setServerVisible(true); final InsetsPolicy policy = spy(mDisplayContent.getInsetsPolicy()); - doNothing().when(policy).startAnimation(anyBoolean(), any(), any()); + doNothing().when(policy).startAnimation(anyBoolean(), any()); policy.updateBarControlTarget(mAppWindow); policy.showTransient(new int[]{ITYPE_STATUS_BAR, ITYPE_NAVIGATION_BAR}); waitUntilWindowAnimatorIdle(); @@ -301,7 +308,7 @@ public class InsetsPolicyTest extends WindowTestsBase { .getControllableInsetProvider().getSource().setVisible(false); final InsetsPolicy policy = spy(mDisplayContent.getInsetsPolicy()); - doNothing().when(policy).startAnimation(anyBoolean(), any(), any()); + doNothing().when(policy).startAnimation(anyBoolean(), any()); policy.updateBarControlTarget(mAppWindow); policy.showTransient(new int[]{ITYPE_STATUS_BAR, ITYPE_NAVIGATION_BAR}); waitUntilWindowAnimatorIdle(); @@ -326,7 +333,8 @@ public class InsetsPolicyTest extends WindowTestsBase { assertTrue(state.getSource(ITYPE_STATUS_BAR).isVisible()); assertTrue(state.getSource(ITYPE_NAVIGATION_BAR).isVisible()); - policy.onInsetsModified(mAppWindow, state); + mAppWindow.updateRequestedVisibility(state); + policy.onInsetsModified(mAppWindow); waitUntilWindowAnimatorIdle(); controls = mDisplayContent.getInsetsStateController().getControlsForDispatch(mAppWindow); @@ -348,7 +356,7 @@ public class InsetsPolicyTest extends WindowTestsBase { final WindowState app2 = addWindow(TYPE_APPLICATION, "app"); final InsetsPolicy policy = spy(mDisplayContent.getInsetsPolicy()); - doNothing().when(policy).startAnimation(anyBoolean(), any(), any()); + doNothing().when(policy).startAnimation(anyBoolean(), any()); policy.updateBarControlTarget(app); policy.showTransient(new int[]{ITYPE_STATUS_BAR, ITYPE_NAVIGATION_BAR}); final InsetsSourceControl[] controls = diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java index a0fa9369e7a8..983063125ce9 100644 --- a/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/InsetsSourceProviderTest.java @@ -205,7 +205,8 @@ public class InsetsSourceProviderTest extends WindowTestsBase { mProvider.updateControlForTarget(target, false /* force */); InsetsState state = new InsetsState(); state.getSource(ITYPE_STATUS_BAR).setVisible(false); - mProvider.onInsetsModified(target, state.getSource(ITYPE_STATUS_BAR)); + target.updateRequestedVisibility(state); + mProvider.updateClientVisibility(target); assertFalse(mSource.isVisible()); } @@ -217,7 +218,8 @@ public class InsetsSourceProviderTest extends WindowTestsBase { mProvider.setWindow(statusBar, null, null); InsetsState state = new InsetsState(); state.getSource(ITYPE_STATUS_BAR).setVisible(false); - mProvider.onInsetsModified(target, state.getSource(ITYPE_STATUS_BAR)); + target.updateRequestedVisibility(state); + mProvider.updateClientVisibility(target); assertTrue(mSource.isVisible()); } diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java index e2cd8a909266..c14df676f525 100644 --- a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java @@ -189,8 +189,8 @@ public class InsetsStateControllerTest extends WindowTestsBase { getController().onImeControlTargetChanged(mDisplayContent.mInputMethodInputTarget); final InsetsState requestedState = new InsetsState(); requestedState.getSource(ITYPE_IME).setVisible(true); - mDisplayContent.mInputMethodInputTarget.updateRequestedInsetsState(requestedState); - getController().onInsetsModified(mDisplayContent.mInputMethodInputTarget, requestedState); + mDisplayContent.mInputMethodInputTarget.updateRequestedVisibility(requestedState); + getController().onInsetsModified(mDisplayContent.mInputMethodInputTarget); // Send our spy window (app) into the system so that we can detect the invocation. final WindowState win = createWindow(null, TYPE_APPLICATION, "app"); diff --git a/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java b/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java index d0a5644f5025..ecbfac8b091b 100644 --- a/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java +++ b/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java @@ -163,12 +163,6 @@ public class StubTransaction extends SurfaceControl.Transaction { } @Override - public SurfaceControl.Transaction setOverrideScalingMode(SurfaceControl sc, - int overrideScalingMode) { - return this; - } - - @Override public SurfaceControl.Transaction setColor(SurfaceControl sc, float[] color) { return this; } diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java index d2b7ac4c3b24..7975899dacbc 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java @@ -1002,14 +1002,18 @@ public class TaskRecordTests extends WindowTestsBase { public void testNotSpecifyOrientationByFloatingTask() { final Task task = getTestTask(); final ActivityRecord activity = task.getTopMostActivity(); + final WindowContainer<?> parentContainer = task.getParent(); final TaskDisplayArea taskDisplayArea = task.getDisplayArea(); activity.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE); + assertEquals(SCREEN_ORIENTATION_LANDSCAPE, parentContainer.getOrientation()); assertEquals(SCREEN_ORIENTATION_LANDSCAPE, taskDisplayArea.getOrientation()); task.setWindowingMode(WINDOWING_MODE_PINNED); - assertEquals(SCREEN_ORIENTATION_UNSET, taskDisplayArea.getOrientation()); + // TDA returns the last orientation when child returns UNSET + assertEquals(SCREEN_ORIENTATION_UNSET, parentContainer.getOrientation()); + assertEquals(SCREEN_ORIENTATION_LANDSCAPE, taskDisplayArea.getOrientation()); } @Test diff --git a/services/tests/wmtests/src/com/android/server/wm/TestIWindow.java b/services/tests/wmtests/src/com/android/server/wm/TestIWindow.java index ea1223312cb2..db1c12f525f3 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TestIWindow.java +++ b/services/tests/wmtests/src/com/android/server/wm/TestIWindow.java @@ -22,7 +22,7 @@ import android.os.ParcelFileDescriptor; import android.os.RemoteException; import android.util.MergedConfiguration; import android.view.DragEvent; -import android.view.IScrollCaptureController; +import android.view.IScrollCaptureCallbacks; import android.view.IWindow; import android.view.InsetsSourceControl; import android.view.InsetsState; @@ -107,7 +107,7 @@ public class TestIWindow extends IWindow.Stub { } @Override - public void requestScrollCapture(IScrollCaptureController controller) throws RemoteException { + public void requestScrollCapture(IScrollCaptureCallbacks callbacks) throws RemoteException { } @Override diff --git a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java index dc859046db42..db5c7965ebee 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java +++ b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java @@ -197,19 +197,19 @@ class TestWindowManagerPolicy implements WindowManagerPolicy { } @Override - public void screenTurningOn(ScreenOnListener screenOnListener) { + public void screenTurningOn(int displayId, ScreenOnListener screenOnListener) { } @Override - public void screenTurnedOn() { + public void screenTurnedOn(int displayId) { } @Override - public void screenTurningOff(ScreenOffListener screenOffListener) { + public void screenTurningOff(int displayId, ScreenOffListener screenOffListener) { } @Override - public void screenTurnedOff() { + public void screenTurnedOff(int displayId) { } @Override diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java index 0152fc607f73..bd586a5f6c76 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java @@ -54,12 +54,14 @@ import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.atLeastOnce; import android.app.ActivityManager; import android.app.ActivityManager.RunningTaskInfo; import android.app.ActivityTaskManager.RootTaskInfo; import android.app.PictureInPictureParams; import android.content.pm.ActivityInfo; +import android.content.pm.ParceledListSlice; import android.content.res.Configuration; import android.graphics.Rect; import android.os.Binder; @@ -72,6 +74,7 @@ import android.view.Display; import android.view.SurfaceControl; import android.window.ITaskOrganizer; import android.window.IWindowContainerTransactionCallback; +import android.window.TaskAppearedInfo; import android.window.WindowContainerTransaction; import androidx.test.filters.SmallTest; @@ -79,8 +82,10 @@ import androidx.test.filters.SmallTest; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; /** @@ -93,14 +98,27 @@ import java.util.List; @Presubmit @RunWith(WindowTestRunner.class) public class WindowOrganizerTests extends WindowTestsBase { - private ITaskOrganizer registerMockOrganizer() { + + private ITaskOrganizer createMockOrganizer() { final ITaskOrganizer organizer = mock(ITaskOrganizer.class); when(organizer.asBinder()).thenReturn(new Binder()); + return organizer; + } - mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer(organizer); + private ITaskOrganizer registerMockOrganizer(ArrayList<TaskAppearedInfo> existingTasks) { + final ITaskOrganizer organizer = createMockOrganizer(); + ParceledListSlice<TaskAppearedInfo> tasks = + mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer(organizer); + if (existingTasks != null) { + existingTasks.addAll(tasks.getList()); + } return organizer; } + private ITaskOrganizer registerMockOrganizer() { + return registerMockOrganizer(null); + } + Task createTask(Task stack, boolean fakeDraw) { final Task task = createTaskInStack(stack, 0); @@ -128,27 +146,21 @@ public class WindowOrganizerTests extends WindowTestsBase { @Test public void testAppearVanish() throws RemoteException { + final ITaskOrganizer organizer = registerMockOrganizer(); final Task stack = createStack(); final Task task = createTask(stack); - final ITaskOrganizer organizer = registerMockOrganizer(); - stack.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW); - stack.setTaskOrganizer(organizer); verify(organizer).onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class)); - stack.removeImmediately(); verify(organizer).onTaskVanished(any()); } @Test public void testAppearWaitsForVisibility() throws RemoteException { + final ITaskOrganizer organizer = registerMockOrganizer(); final Task stack = createStack(); final Task task = createTask(stack, false); - final ITaskOrganizer organizer = registerMockOrganizer(); - - stack.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW); - stack.setTaskOrganizer(organizer); verify(organizer, never()) .onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class)); @@ -163,9 +175,9 @@ public class WindowOrganizerTests extends WindowTestsBase { @Test public void testNoVanishedIfNoAppear() throws RemoteException { + final ITaskOrganizer organizer = registerMockOrganizer(); final Task stack = createStack(); final Task task = createTask(stack, false /* hasBeenVisible */); - final ITaskOrganizer organizer = registerMockOrganizer(); // In this test we skip making the Task visible, and verify // that even though a TaskOrganizer is set remove doesn't emit @@ -179,28 +191,25 @@ public class WindowOrganizerTests extends WindowTestsBase { @Test public void testTaskNoDraw() throws RemoteException { + final ITaskOrganizer organizer = registerMockOrganizer(); final Task stack = createStack(); final Task task = createTask(stack, false /* fakeDraw */); - final ITaskOrganizer organizer = registerMockOrganizer(); - stack.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW); verify(organizer, never()) .onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class)); assertTrue(stack.isOrganized()); mWm.mAtmService.mTaskOrganizerController.unregisterTaskOrganizer(organizer); - verify(organizer, never()).onTaskVanished(any()); + assertTaskVanished(organizer, false /* expectVanished */, stack); assertFalse(stack.isOrganized()); } @Test public void testClearOrganizer() throws RemoteException { + final ITaskOrganizer organizer = registerMockOrganizer(); final Task stack = createStack(); final Task task = createTask(stack); - final ITaskOrganizer organizer = registerMockOrganizer(); - stack.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW); - stack.setTaskOrganizer(organizer); verify(organizer).onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class)); assertTrue(stack.isOrganized()); @@ -211,16 +220,15 @@ public class WindowOrganizerTests extends WindowTestsBase { @Test public void testUnregisterOrganizer() throws RemoteException { + final ITaskOrganizer organizer = registerMockOrganizer(); final Task stack = createStack(); final Task task = createTask(stack); - final ITaskOrganizer organizer = registerMockOrganizer(); - stack.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW); verify(organizer).onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class)); assertTrue(stack.isOrganized()); mWm.mAtmService.mTaskOrganizerController.unregisterTaskOrganizer(organizer); - verify(organizer).onTaskVanished(any()); + assertTaskVanished(organizer, true /* expectVanished */, stack); assertFalse(stack.isOrganized()); } @@ -232,37 +240,47 @@ public class WindowOrganizerTests extends WindowTestsBase { final Task task2 = createTask(stack2); final Task stack3 = createStack(); final Task task3 = createTask(stack3); - final ITaskOrganizer organizer = registerMockOrganizer(); - - // verify that tasks are appeared on registration - verify(organizer, times(3)) - .onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class)); + final ArrayList<TaskAppearedInfo> existingTasks = new ArrayList<>(); + final ITaskOrganizer organizer = registerMockOrganizer(existingTasks); + + // verify that tasks are returned and taskAppeared is not called + assertContainsTasks(existingTasks, stack, stack2, stack3); + verify(organizer, times(0)).onTaskAppeared(any(RunningTaskInfo.class), + any(SurfaceControl.class)); + verify(organizer, times(0)).onTaskVanished(any()); assertTrue(stack.isOrganized()); - // Now we replace the registration and1 verify the new organizer receives tasks - final ITaskOrganizer organizer2 = registerMockOrganizer(); - verify(organizer2, times(3)) - .onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class)); + // Now we replace the registration and verify the new organizer receives existing tasks + final ArrayList<TaskAppearedInfo> existingTasks2 = new ArrayList<>(); + final ITaskOrganizer organizer2 = registerMockOrganizer(existingTasks2); + assertContainsTasks(existingTasks2, stack, stack2, stack3); + verify(organizer2, times(0)).onTaskAppeared(any(RunningTaskInfo.class), + any(SurfaceControl.class)); verify(organizer2, times(0)).onTaskVanished(any()); - // One for task - verify(organizer, times(3)).onTaskVanished(any()); + // Removed tasks from the original organizer + assertTaskVanished(organizer, true /* expectVanished */, stack, stack2, stack3); assertTrue(stack2.isOrganized()); // Now we unregister the second one, the first one should automatically be reregistered // so we verify that it's now seeing changes. mWm.mAtmService.mTaskOrganizerController.unregisterTaskOrganizer(organizer2); - verify(organizer, times(6)) + verify(organizer, times(3)) .onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class)); - verify(organizer2, times(3)).onTaskVanished(any()); + assertTaskVanished(organizer2, true /* expectVanished */, stack, stack2, stack3); } @Test public void testRegisterTaskOrganizerWithExistingTasks() throws RemoteException { final Task stack = createStack(); final Task task = createTask(stack); + final Task stack2 = createStack(); + final Task task2 = createTask(stack2); + ArrayList<TaskAppearedInfo> existingTasks = new ArrayList<>(); + final ITaskOrganizer organizer = registerMockOrganizer(existingTasks); + assertContainsTasks(existingTasks, stack, stack2); - final ITaskOrganizer organizer = registerMockOrganizer(); - verify(organizer, times(1)) + // Verify we don't get onTaskAppeared if we are returned the tasks + verify(organizer, never()) .onTaskAppeared(any(RunningTaskInfo.class), any(SurfaceControl.class)); } @@ -366,7 +384,7 @@ public class WindowOrganizerTests extends WindowTestsBase { } @Test - public void testSetIgnoreOrientationRequest() { + public void testSetIgnoreOrientationRequest_taskDisplayArea() { removeGlobalMinSizeRestriction(); final TaskDisplayArea taskDisplayArea = mDisplayContent.getDefaultTaskDisplayArea(); final Task stack = taskDisplayArea.createStack( @@ -378,7 +396,7 @@ public class WindowOrganizerTests extends WindowTestsBase { activity.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE); // TDA returns UNSET when ignoreOrientationRequest == true - // DC is UNSPECIFIED because it is using the previous (default) when TDA returns UNSET. + // DC is UNSPECIFIED when child returns UNSET assertThat(taskDisplayArea.getOrientation()).isEqualTo(SCREEN_ORIENTATION_UNSET); assertThat(mDisplayContent.getLastOrientation()).isEqualTo(SCREEN_ORIENTATION_UNSPECIFIED); @@ -399,8 +417,40 @@ public class WindowOrganizerTests extends WindowTestsBase { mWm.mAtmService.mWindowOrganizerController.applyTransaction(t); // TDA returns UNSET when ignoreOrientationRequest == true - // DC is LANDSCAPE because it is using the previous when TDA returns UNSET. + // DC is UNSPECIFIED when child returns UNSET assertThat(taskDisplayArea.getOrientation()).isEqualTo(SCREEN_ORIENTATION_UNSET); + assertThat(mDisplayContent.getLastOrientation()).isEqualTo(SCREEN_ORIENTATION_UNSPECIFIED); + } + + @Test + public void testSetIgnoreOrientationRequest_displayContent() { + removeGlobalMinSizeRestriction(); + final TaskDisplayArea taskDisplayArea = mDisplayContent.getDefaultTaskDisplayArea(); + final Task stack = taskDisplayArea.createStack( + WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */); + final ActivityRecord activity = new ActivityBuilder(mAtm).setCreateTask(true) + .setStack(stack).build(); + mDisplayContent.setFocusedApp(activity); + activity.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE); + + // DC uses the orientation request from app + assertThat(mDisplayContent.getLastOrientation()).isEqualTo(SCREEN_ORIENTATION_LANDSCAPE); + + WindowContainerTransaction t = new WindowContainerTransaction(); + t.setIgnoreOrientationRequest( + mDisplayContent.mRemoteToken.toWindowContainerToken(), + true /* ignoreOrientationRequest */); + mWm.mAtmService.mWindowOrganizerController.applyTransaction(t); + + // DC returns UNSPECIFIED when ignoreOrientationRequest == true + assertThat(mDisplayContent.getLastOrientation()).isEqualTo(SCREEN_ORIENTATION_UNSPECIFIED); + + t.setIgnoreOrientationRequest( + mDisplayContent.mRemoteToken.toWindowContainerToken(), + false /* ignoreOrientationRequest */); + mWm.mAtmService.mWindowOrganizerController.applyTransaction(t); + + // DC uses the orientation request from app after mIgnoreOrientationRequest is set to false assertThat(mDisplayContent.getLastOrientation()).isEqualTo(SCREEN_ORIENTATION_LANDSCAPE); } @@ -429,16 +479,33 @@ public class WindowOrganizerTests extends WindowTestsBase { @Test public void testCreateDeleteRootTasks() { + ITaskOrganizer listener = new ITaskOrganizer.Stub() { + @Override + public void onTaskAppeared(RunningTaskInfo taskInfo, SurfaceControl leash) { } + + @Override + public void onTaskVanished(RunningTaskInfo container) { } + + @Override + public void onTaskInfoChanged(RunningTaskInfo info) { + } + + @Override + public void onBackPressedOnTaskRoot(RunningTaskInfo taskInfo) { + } + }; + mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer(listener); + RunningTaskInfo info1 = mWm.mAtmService.mTaskOrganizerController.createRootTask( Display.DEFAULT_DISPLAY, - WINDOWING_MODE_SPLIT_SCREEN_PRIMARY); + WINDOWING_MODE_SPLIT_SCREEN_PRIMARY).getTaskInfo(); assertEquals(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, info1.configuration.windowConfiguration.getWindowingMode()); assertEquals(ACTIVITY_TYPE_UNDEFINED, info1.topActivityType); RunningTaskInfo info2 = mWm.mAtmService.mTaskOrganizerController.createRootTask( Display.DEFAULT_DISPLAY, - WINDOWING_MODE_SPLIT_SCREEN_SECONDARY); + WINDOWING_MODE_SPLIT_SCREEN_SECONDARY).getTaskInfo(); assertEquals(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, info2.configuration.windowConfiguration.getWindowingMode()); assertEquals(ACTIVITY_TYPE_UNDEFINED, info2.topActivityType); @@ -472,7 +539,7 @@ public class WindowOrganizerTests extends WindowTestsBase { }; mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer(listener); RunningTaskInfo info1 = mWm.mAtmService.mTaskOrganizerController.createRootTask( - mDisplayContent.mDisplayId, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY); + mDisplayContent.mDisplayId, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY).getTaskInfo(); final Task stack = createTaskStackOnDisplay( WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD, mDisplayContent); @@ -530,7 +597,7 @@ public class WindowOrganizerTests extends WindowTestsBase { }; mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer(listener); RunningTaskInfo info1 = mWm.mAtmService.mTaskOrganizerController.createRootTask( - mDisplayContent.mDisplayId, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY); + mDisplayContent.mDisplayId, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY).getTaskInfo(); lastReportedTiles.clear(); called[0] = false; @@ -591,9 +658,9 @@ public class WindowOrganizerTests extends WindowTestsBase { }; mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer(listener); RunningTaskInfo info1 = mWm.mAtmService.mTaskOrganizerController.createRootTask( - mDisplayContent.mDisplayId, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY); + mDisplayContent.mDisplayId, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY).getTaskInfo(); RunningTaskInfo info2 = mWm.mAtmService.mTaskOrganizerController.createRootTask( - mDisplayContent.mDisplayId, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY); + mDisplayContent.mDisplayId, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY).getTaskInfo(); final int initialRootTaskCount = mWm.mAtmService.mTaskOrganizerController.getRootTasks( mDisplayContent.mDisplayId, null /* activityTypes */).size(); @@ -922,9 +989,9 @@ public class WindowOrganizerTests extends WindowTestsBase { @Test public void testPreventDuplicateAppear() throws RemoteException { + final ITaskOrganizer organizer = registerMockOrganizer(); final Task stack = createStack(); final Task task = createTask(stack, false /* fakeDraw */); - final ITaskOrganizer organizer = registerMockOrganizer(); stack.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW); stack.setTaskOrganizer(organizer); @@ -945,17 +1012,14 @@ public class WindowOrganizerTests extends WindowTestsBase { @Test public void testInterceptBackPressedOnTaskRoot() throws RemoteException { + final ITaskOrganizer organizer = registerMockOrganizer(); final Task stack = createStack(); final Task task = createTask(stack); final ActivityRecord activity = createActivityRecordInTask(stack.mDisplayContent, task); final Task stack2 = createStack(); final Task task2 = createTask(stack2); final ActivityRecord activity2 = createActivityRecordInTask(stack.mDisplayContent, task2); - final ITaskOrganizer organizer = registerMockOrganizer(); - // Setup the task to be controlled by the MW mode organizer - stack.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW); - stack2.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW); assertTrue(stack.isOrganized()); assertTrue(stack2.isOrganized()); @@ -982,9 +1046,9 @@ public class WindowOrganizerTests extends WindowTestsBase { @Test public void testBLASTCallbackWithMultipleWindows() throws Exception { + final ITaskOrganizer organizer = registerMockOrganizer(); final Task stackController = createStack(); final Task task = createTask(stackController); - final ITaskOrganizer organizer = registerMockOrganizer(); final WindowState w1 = createAppWindow(task, TYPE_APPLICATION, "Enlightened Window 1"); final WindowState w2 = createAppWindow(task, TYPE_APPLICATION, "Enlightened Window 2"); makeWindowVisible(w1); @@ -1035,4 +1099,37 @@ public class WindowOrganizerTests extends WindowTestsBase { assertFalse(daTask.isForceHidden()); }); } + + /** + * Verifies that task vanished is called for a specific task. + */ + private void assertTaskVanished(ITaskOrganizer organizer, boolean expectVanished, Task... tasks) + throws RemoteException { + ArgumentCaptor<RunningTaskInfo> arg = ArgumentCaptor.forClass(RunningTaskInfo.class); + verify(organizer, atLeastOnce()).onTaskVanished(arg.capture()); + List<RunningTaskInfo> taskInfos = arg.getAllValues(); + + HashSet<Integer> vanishedTaskIds = new HashSet<>(); + for (int i = 0; i < taskInfos.size(); i++) { + vanishedTaskIds.add(taskInfos.get(i).taskId); + } + HashSet<Integer> taskIds = new HashSet<>(); + for (int i = 0; i < tasks.length; i++) { + taskIds.add(tasks[i].mTaskId); + } + + assertTrue(expectVanished + ? vanishedTaskIds.containsAll(taskIds) + : !vanishedTaskIds.removeAll(taskIds)); + } + + private void assertContainsTasks(List<TaskAppearedInfo> taskInfos, Task... expectedTasks) { + HashSet<Integer> taskIds = new HashSet<>(); + for (int i = 0; i < taskInfos.size(); i++) { + taskIds.add(taskInfos.get(i).getTaskInfo().taskId); + } + for (int i = 0; i < expectedTasks.length; i++) { + assertTrue(taskIds.contains(expectedTasks[i].mTaskId)); + } + } } diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java index c18043fcc4b9..19bed48a4bc5 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java @@ -71,7 +71,7 @@ import android.os.RemoteException; import android.platform.test.annotations.Presubmit; import android.util.Size; import android.view.DisplayCutout; -import android.view.InsetsSource; +import android.view.InsetsState; import android.view.SurfaceControl; import android.view.WindowManager; @@ -426,10 +426,11 @@ public class WindowStateTests extends WindowTestsBase { .setWindow(statusBar, null /* frameProvider */, null /* imeFrameProvider */); mDisplayContent.getInsetsStateController().onBarControlTargetChanged( app, null /* fakeTopControlling */, app, null /* fakeNavControlling */); - final InsetsSource source = new InsetsSource(ITYPE_STATUS_BAR); - source.setVisible(false); + final InsetsState state = new InsetsState(); + state.getSource(ITYPE_STATUS_BAR).setVisible(false); + app.updateRequestedVisibility(state); mDisplayContent.getInsetsStateController().getSourceProvider(ITYPE_STATUS_BAR) - .onInsetsModified(app, source); + .updateClientVisibility(app); waitUntilHandlersIdle(); assertFalse(statusBar.isVisible()); } diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java index 6237be0f4b26..924b286a4f8e 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java @@ -1006,10 +1006,10 @@ class WindowTestsBase extends SystemServiceTestsBase { mDisplayId = displayId; mService.mTaskOrganizerController.registerTaskOrganizer(this); WindowContainerToken primary = mService.mTaskOrganizerController.createRootTask( - displayId, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY).token; + displayId, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY).getTaskInfo().token; mPrimary = WindowContainer.fromBinder(primary.asBinder()).asTask(); WindowContainerToken secondary = mService.mTaskOrganizerController.createRootTask( - displayId, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY).token; + displayId, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY).getTaskInfo().token; mSecondary = WindowContainer.fromBinder(secondary.asBinder()).asTask(); } TestSplitOrganizer(ActivityTaskManagerService service) { diff --git a/services/usb/java/com/android/server/usb/MtpNotificationManager.java b/services/usb/java/com/android/server/usb/MtpNotificationManager.java index 462ee19124ff..8845f118f3a8 100644 --- a/services/usb/java/com/android/server/usb/MtpNotificationManager.java +++ b/services/usb/java/com/android/server/usb/MtpNotificationManager.java @@ -64,12 +64,13 @@ class MtpNotificationManager { private final Context mContext; private final OnOpenInAppListener mListener; + private final Receiver mReceiver; MtpNotificationManager(Context context, OnOpenInAppListener listener) { mContext = context; mListener = listener; - final Receiver receiver = new Receiver(); - context.registerReceiver(receiver, new IntentFilter(ACTION_OPEN_IN_APPS)); + mReceiver = new Receiver(); + context.registerReceiver(mReceiver, new IntentFilter(ACTION_OPEN_IN_APPS)); } void showNotification(UsbDevice device) { @@ -90,11 +91,12 @@ class MtpNotificationManager { intent.putExtra(UsbManager.EXTRA_DEVICE, device); intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_FOREGROUND); + // Simple notification clicks are immutable final PendingIntent openIntent = PendingIntent.getBroadcastAsUser( mContext, device.getDeviceId(), intent, - PendingIntent.FLAG_UPDATE_CURRENT, + PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE, UserHandle.SYSTEM); builder.setContentIntent(openIntent); @@ -154,4 +156,8 @@ class MtpNotificationManager { static interface OnOpenInAppListener { void onOpenInApp(UsbDevice device); } + + public void unregister() { + mContext.unregisterReceiver(mReceiver); + } } diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java index 2269e1d070b1..58859e095554 100644 --- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java +++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java @@ -1206,8 +1206,9 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser Intent intent = Intent.makeRestartActivityTask( new ComponentName("com.android.settings", "com.android.settings.Settings$UsbDetailsActivity")); + // Simple notification clicks are immutable pi = PendingIntent.getActivityAsUser(mContext, 0, - intent, 0, null, UserHandle.CURRENT); + intent, PendingIntent.FLAG_IMMUTABLE, null, UserHandle.CURRENT); channel = SystemNotificationChannels.USB; } else { final Intent intent = new Intent(); @@ -1217,7 +1218,9 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser "help_url_audio_accessory_not_supported"); if (mContext.getPackageManager().resolveActivity(intent, 0) != null) { - pi = PendingIntent.getActivity(mContext, 0, intent, 0); + // Simple notification clicks are immutable + pi = PendingIntent.getActivity(mContext, 0, intent, + PendingIntent.FLAG_IMMUTABLE); } else { pi = null; } diff --git a/services/usb/java/com/android/server/usb/UsbPortManager.java b/services/usb/java/com/android/server/usb/UsbPortManager.java index ec7d4bd0d8c0..ca18c57d6f4b 100644 --- a/services/usb/java/com/android/server/usb/UsbPortManager.java +++ b/services/usb/java/com/android/server/usb/UsbPortManager.java @@ -230,8 +230,9 @@ public class UsbPortManager { com.android.internal.R.string.config_usbContaminantActivity))); intent.putExtra(UsbManager.EXTRA_PORT, ParcelableUsbPort.of(currentPortInfo.mUsbPort)); + // Simple notification clicks are immutable PendingIntent pi = PendingIntent.getActivityAsUser(mContext, 0, - intent, 0, null, UserHandle.CURRENT); + intent, PendingIntent.FLAG_IMMUTABLE, null, UserHandle.CURRENT); Notification.Builder builder = new Notification.Builder(mContext, channel) .setOngoing(true) diff --git a/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java b/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java index d7b6b5d0d36a..26ee03c25013 100644 --- a/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java +++ b/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java @@ -261,6 +261,15 @@ class UsbProfileGroupSettingsManager { } /** + * Unregister all broadcast receivers. Must be called explicitly before + * object deletion. + */ + public void unregisterReceivers() { + mPackageMonitor.unregister(); + mMtpNotificationManager.unregister(); + } + + /** * Remove all defaults and denied packages for a user. * * @param userToRemove The user diff --git a/services/usb/java/com/android/server/usb/UsbSettingsManager.java b/services/usb/java/com/android/server/usb/UsbSettingsManager.java index 7b677eea6b8f..8e53ff412f0a 100644 --- a/services/usb/java/com/android/server/usb/UsbSettingsManager.java +++ b/services/usb/java/com/android/server/usb/UsbSettingsManager.java @@ -124,6 +124,7 @@ class UsbSettingsManager { if (mSettingsByProfileGroup.indexOfKey(userToRemove.getIdentifier()) >= 0) { // The user to remove is the parent user of the group. The parent is the last user // that gets removed. All state will be removed with the user + mSettingsByProfileGroup.get(userToRemove.getIdentifier()).unregisterReceivers(); mSettingsByProfileGroup.remove(userToRemove.getIdentifier()); } else { // We cannot find the parent user of the user that is removed, hence try to remove diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java index 917f65ab7c01..547d253501e8 100644 --- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java @@ -1048,9 +1048,9 @@ public class VoiceInteractionManagerService extends SystemService { if (unloadStatus != SoundTriggerInternal.STATUS_OK) { Slog.w(TAG, "Unable to unload keyphrase sound model:" + unloadStatus); } - deleted = mDbHelper.deleteKeyphraseSoundModel(keyphraseId, callingUserId, - bcp47Locale); } + deleted = mDbHelper.deleteKeyphraseSoundModel(keyphraseId, callingUserId, + bcp47Locale); return deleted ? SoundTriggerInternal.STATUS_OK : SoundTriggerInternal.STATUS_ERROR; } finally { if (deleted) { diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java index 9f16543c410e..a85eb53605d6 100755 --- a/telecomm/java/android/telecom/Call.java +++ b/telecomm/java/android/telecom/Call.java @@ -1683,7 +1683,6 @@ public final class Call { * @hide */ @SystemApi - @TestApi public void enterBackgroundAudioProcessing() { if (mState != STATE_ACTIVE && mState != STATE_RINGING) { throw new IllegalStateException("Call must be active or ringing"); @@ -1704,7 +1703,6 @@ public final class Call { * @hide */ @SystemApi - @TestApi public void exitBackgroundAudioProcessing(boolean shouldRing) { if (mState != STATE_AUDIO_PROCESSING) { throw new IllegalStateException("Call must in the audio processing state"); diff --git a/telecomm/java/android/telecom/CallScreeningService.java b/telecomm/java/android/telecom/CallScreeningService.java index 49f183151e27..7988b036ccd3 100644 --- a/telecomm/java/android/telecom/CallScreeningService.java +++ b/telecomm/java/android/telecom/CallScreeningService.java @@ -21,7 +21,6 @@ import android.annotation.NonNull; import android.annotation.RequiresPermission; import android.annotation.SdkConstant; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.app.Service; import android.content.ComponentName; import android.content.Intent; @@ -323,7 +322,6 @@ public abstract class CallScreeningService extends Service { * @hide */ @SystemApi - @TestApi @RequiresPermission(Manifest.permission.CAPTURE_AUDIO_OUTPUT) public @NonNull Builder setShouldScreenCallViaAudioProcessing( boolean shouldScreenCallViaAudioProcessing) { diff --git a/telecomm/java/android/telecom/CallerInfo.java b/telecomm/java/android/telecom/CallerInfo.java index fb6f99405759..aff2f0183a3b 100644 --- a/telecomm/java/android/telecom/CallerInfo.java +++ b/telecomm/java/android/telecom/CallerInfo.java @@ -405,7 +405,8 @@ public class CallerInfo { // Change the callerInfo number ONLY if it is an emergency number // or if it is the voicemail number. If it is either, take a // shortcut and skip the query. - if (PhoneNumberUtils.isLocalEmergencyNumber(context, number)) { + TelephonyManager tm = context.getSystemService(TelephonyManager.class); + if (tm.isEmergencyNumber(number)) { return new CallerInfo().markAsEmergency(context); } else if (PhoneNumberUtils.isVoiceMailNumber(null, subId, number)) { return new CallerInfo().markAsVoiceMail(context, subId); diff --git a/telecomm/java/android/telecom/CallerInfoAsyncQuery.java b/telecomm/java/android/telecom/CallerInfoAsyncQuery.java index 4a81a8eea5cf..a9e1a8fc1952 100644 --- a/telecomm/java/android/telecom/CallerInfoAsyncQuery.java +++ b/telecomm/java/android/telecom/CallerInfoAsyncQuery.java @@ -34,6 +34,7 @@ import android.os.UserManager; import android.provider.ContactsContract.PhoneLookup; import android.telephony.PhoneNumberUtils; import android.telephony.SubscriptionManager; +import android.telephony.TelephonyManager; import android.text.TextUtils; import java.util.ArrayList; @@ -481,7 +482,8 @@ public class CallerInfoAsyncQuery { cw.subId = subId; // check to see if these are recognized numbers, and use shortcuts if we can. - if (PhoneNumberUtils.isLocalEmergencyNumber(context, number)) { + TelephonyManager tm = context.getSystemService(TelephonyManager.class); + if (tm.isEmergencyNumber(number)) { cw.event = EVENT_EMERGENCY_NUMBER; } else if (PhoneNumberUtils.isVoiceMailNumber(context, subId, number)) { cw.event = EVENT_VOICEMAIL_NUMBER; diff --git a/telecomm/java/android/telecom/Conference.java b/telecomm/java/android/telecom/Conference.java index 39c3ff9e8839..dc2fb948fdbe 100644 --- a/telecomm/java/android/telecom/Conference.java +++ b/telecomm/java/android/telecom/Conference.java @@ -24,7 +24,6 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.net.Uri; import android.os.Bundle; import android.os.SystemClock; @@ -137,7 +136,6 @@ public abstract class Conference extends Conferenceable { * @hide */ @SystemApi - @TestApi public final @NonNull String getTelecomCallId() { return mTelecomCallId; } @@ -609,7 +607,6 @@ public abstract class Conference extends Conferenceable { * @return The primary connection. * @hide */ - @TestApi @SystemApi public Connection getPrimaryConnection() { if (mUnmodifiableChildConnections == null || mUnmodifiableChildConnections.isEmpty()) { @@ -1012,7 +1009,6 @@ public abstract class Conference extends Conferenceable { * @hide */ @SystemApi - @TestApi @RequiresPermission(MODIFY_PHONE_STATE) public void setConferenceState(boolean isConference) { mIsMultiparty = isConference; @@ -1067,7 +1063,6 @@ public abstract class Conference extends Conferenceable { * @hide */ @SystemApi - @TestApi @RequiresPermission(MODIFY_PHONE_STATE) public final void setAddress(@NonNull Uri address, @TelecomManager.Presentation int presentation) { @@ -1155,7 +1150,6 @@ public abstract class Conference extends Conferenceable { * @hide */ @SystemApi - @TestApi public final void setCallerDisplayName(@NonNull String callerDisplayName, @TelecomManager.Presentation int presentation) { Log.d(this, "setCallerDisplayName %s", callerDisplayName); diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java index 00b711643fe6..bbf34df8fe84 100755..100644 --- a/telecomm/java/android/telecom/Connection.java +++ b/telecomm/java/android/telecom/Connection.java @@ -25,7 +25,6 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.app.Notification; import android.bluetooth.BluetoothDevice; import android.compat.annotation.UnsupportedAppUsage; @@ -307,7 +306,6 @@ public abstract class Connection extends Conferenceable { * @hide */ @SystemApi - @TestApi public static final int CAPABILITY_SPEED_UP_MT_AUDIO = 0x00040000; /** @@ -345,7 +343,6 @@ public abstract class Connection extends Conferenceable { * @hide */ @SystemApi - @TestApi public static final int CAPABILITY_CONFERENCE_HAS_NO_CHILDREN = 0x00200000; /** @@ -417,7 +414,6 @@ public abstract class Connection extends Conferenceable { * @hide */ @SystemApi - @TestApi public static final int PROPERTY_EMERGENCY_CALLBACK_MODE = 1<<0; /** @@ -428,7 +424,6 @@ public abstract class Connection extends Conferenceable { * @hide */ @SystemApi - @TestApi public static final int PROPERTY_GENERIC_CONFERENCE = 1<<1; /** @@ -480,7 +475,6 @@ public abstract class Connection extends Conferenceable { * @hide */ @SystemApi - @TestApi public static final int PROPERTY_IS_DOWNGRADED_CONFERENCE = 1<<6; /** @@ -524,7 +518,6 @@ public abstract class Connection extends Conferenceable { * @hide */ @SystemApi - @TestApi public static final int PROPERTY_REMOTELY_HOSTED = 1 << 11; /** @@ -702,7 +695,6 @@ public abstract class Connection extends Conferenceable { * @hide */ @SystemApi - @TestApi public static final String EXTRA_DISABLE_ADD_CALL = "android.telecom.extra.DISABLE_ADD_CALL"; @@ -2054,7 +2046,6 @@ public abstract class Connection extends Conferenceable { * @hide */ @SystemApi - @TestApi public final @Nullable String getTelecomCallId() { return mTelecomCallId; } @@ -2171,7 +2162,6 @@ public abstract class Connection extends Conferenceable { * @hide */ @SystemApi - @TestApi public final @IntRange(from = 0) long getConnectTimeMillis() { return mConnectTimeMillis; } @@ -2196,7 +2186,6 @@ public abstract class Connection extends Conferenceable { * @hide */ @SystemApi - @TestApi public final @ElapsedRealtimeLong long getConnectionStartElapsedRealtimeMillis() { return mConnectElapsedTimeMillis; } @@ -2279,7 +2268,6 @@ public abstract class Connection extends Conferenceable { * @hide */ @SystemApi - @TestApi public void setTelecomCallId(@NonNull String callId) { mTelecomCallId = callId; } @@ -2628,7 +2616,6 @@ public abstract class Connection extends Conferenceable { * @hide */ @SystemApi - @TestApi @RequiresPermission(MODIFY_PHONE_STATE) public final void setConnectTimeMillis(@IntRange(from = 0) long connectTimeMillis) { mConnectTimeMillis = connectTimeMillis; @@ -2651,7 +2638,6 @@ public abstract class Connection extends Conferenceable { * @hide */ @SystemApi - @TestApi @RequiresPermission(MODIFY_PHONE_STATE) public final void setConnectionStartElapsedRealtimeMillis( @ElapsedRealtimeLong long connectElapsedTimeMillis) { @@ -2722,7 +2708,6 @@ public abstract class Connection extends Conferenceable { * @hide */ @SystemApi - @TestApi public final void resetConnectionTime() { for (Listener l : mListeners) { l.onConnectionTimeReset(this); @@ -3505,7 +3490,6 @@ public abstract class Connection extends Conferenceable { * @hide */ @SystemApi - @TestApi public void setPhoneAccountHandle(@NonNull PhoneAccountHandle phoneAccountHandle) { if (mPhoneAccountHandle != phoneAccountHandle) { mPhoneAccountHandle = phoneAccountHandle; @@ -3524,7 +3508,6 @@ public abstract class Connection extends Conferenceable { * @hide */ @SystemApi - @TestApi public @Nullable PhoneAccountHandle getPhoneAccountHandle() { return mPhoneAccountHandle; } @@ -3590,7 +3573,6 @@ public abstract class Connection extends Conferenceable { * @hide */ @SystemApi - @TestApi public void setCallDirection(@Call.Details.CallDirection int callDirection) { mCallDirection = callDirection; } diff --git a/telecomm/java/android/telecom/ConnectionRequest.java b/telecomm/java/android/telecom/ConnectionRequest.java index 6d7ceca0a2cd..b73ef9b794e4 100644 --- a/telecomm/java/android/telecom/ConnectionRequest.java +++ b/telecomm/java/android/telecom/ConnectionRequest.java @@ -327,7 +327,6 @@ public final class ConnectionRequest implements Parcelable { * @hide */ @SystemApi - @TestApi public @Nullable String getTelecomCallId() { return mTelecomCallId; } diff --git a/telecomm/java/android/telecom/PhoneAccount.java b/telecomm/java/android/telecom/PhoneAccount.java index 866e17148a1a..5024ae27ee49 100644 --- a/telecomm/java/android/telecom/PhoneAccount.java +++ b/telecomm/java/android/telecom/PhoneAccount.java @@ -21,7 +21,6 @@ import static android.Manifest.permission.MODIFY_PHONE_STATE; import android.annotation.NonNull; import android.annotation.RequiresPermission; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.content.Intent; import android.graphics.drawable.Icon; import android.net.Uri; @@ -635,7 +634,6 @@ public final class PhoneAccount implements Parcelable { * @hide */ @SystemApi - @TestApi @RequiresPermission(MODIFY_PHONE_STATE) public @NonNull Builder setGroupId(@NonNull String groupId) { if (groupId != null) { diff --git a/telecomm/java/android/telecom/PhoneAccountSuggestionService.java b/telecomm/java/android/telecom/PhoneAccountSuggestionService.java index ba3822cb9951..8a91b9e9ee81 100644 --- a/telecomm/java/android/telecom/PhoneAccountSuggestionService.java +++ b/telecomm/java/android/telecom/PhoneAccountSuggestionService.java @@ -19,7 +19,6 @@ package android.telecom; import android.annotation.NonNull; import android.annotation.SdkConstant; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.app.Service; import android.content.Intent; import android.os.IBinder; @@ -57,7 +56,6 @@ import java.util.Map; * @hide */ @SystemApi -@TestApi public class PhoneAccountSuggestionService extends Service { /** * The {@link Intent} that must be declared in the {@code intent-filter} element of the diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java index f1deec60aca7..82da4475c1b9 100644 --- a/telecomm/java/android/telecom/TelecomManager.java +++ b/telecomm/java/android/telecom/TelecomManager.java @@ -25,7 +25,6 @@ import android.annotation.SuppressAutoDoc; import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.annotation.SystemService; -import android.annotation.TestApi; import android.compat.annotation.UnsupportedAppUsage; import android.content.ComponentName; import android.content.Context; @@ -693,7 +692,6 @@ public class TelecomManager { * * @hide */ - @TestApi @SystemApi public static final int TTY_MODE_OFF = 0; @@ -703,7 +701,6 @@ public class TelecomManager { * * @hide */ - @TestApi @SystemApi public static final int TTY_MODE_FULL = 1; @@ -714,7 +711,6 @@ public class TelecomManager { * * @hide */ - @TestApi @SystemApi public static final int TTY_MODE_HCO = 2; @@ -725,7 +721,6 @@ public class TelecomManager { * * @hide */ - @TestApi @SystemApi public static final int TTY_MODE_VCO = 3; @@ -736,7 +731,6 @@ public class TelecomManager { * TTY mode. * @hide */ - @TestApi @SystemApi public static final String ACTION_CURRENT_TTY_MODE_CHANGED = "android.telecom.action.CURRENT_TTY_MODE_CHANGED"; @@ -759,7 +753,6 @@ public class TelecomManager { * plugged into the device. * @hide */ - @TestApi @SystemApi public static final String EXTRA_CURRENT_TTY_MODE = "android.telecom.extra.CURRENT_TTY_MODE"; @@ -771,7 +764,6 @@ public class TelecomManager { * preferred TTY mode. * @hide */ - @TestApi @SystemApi public static final String ACTION_TTY_PREFERRED_MODE_CHANGED = "android.telecom.action.TTY_PREFERRED_MODE_CHANGED"; @@ -790,7 +782,6 @@ public class TelecomManager { * </ul> * @hide */ - @TestApi @SystemApi public static final String EXTRA_TTY_PREFERRED_MODE = "android.telecom.extra.TTY_PREFERRED_MODE"; @@ -1045,7 +1036,6 @@ public class TelecomManager { * @hide */ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) - @TestApi @SystemApi public void setUserSelectedOutgoingPhoneAccount(@Nullable PhoneAccountHandle accountHandle) { try { @@ -1219,7 +1209,6 @@ public class TelecomManager { * @hide */ @SystemApi - @TestApi @RequiresPermission(READ_PRIVILEGED_PHONE_STATE) public @NonNull List<PhoneAccountHandle> getCallCapablePhoneAccounts( boolean includeDisabledAccounts) { @@ -1453,7 +1442,6 @@ public class TelecomManager { * @hide */ @SystemApi - @TestApi @RequiresPermission(READ_PRIVILEGED_PHONE_STATE) public @Nullable String getDefaultDialerPackage(@NonNull UserHandle userHandle) { try { @@ -1677,7 +1665,6 @@ public class TelecomManager { * @hide */ @SystemApi - @TestApi @RequiresPermission(anyOf = { READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE @@ -1835,7 +1822,6 @@ public class TelecomManager { * @hide */ @SystemApi - @TestApi @RequiresPermission(READ_PRIVILEGED_PHONE_STATE) public @TtyMode int getCurrentTtyMode() { try { @@ -2249,7 +2235,6 @@ public class TelecomManager { * @hide */ @SystemApi - @TestApi @NonNull public Intent createLaunchEmergencyDialerIntent(@Nullable String number) { ITelecomService service = getTelecomService(); @@ -2402,7 +2387,6 @@ public class TelecomManager { * @hide */ @SystemApi - @TestApi @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean isInEmergencyCall() { try { diff --git a/telephony/api/system-current.txt b/telephony/api/system-current.txt index 4fb7302a5c5f..228f98e489e4 100644 --- a/telephony/api/system-current.txt +++ b/telephony/api/system-current.txt @@ -227,6 +227,25 @@ package android.telephony { field public static final String MBMS_STREAMING_SERVICE_ACTION = "android.telephony.action.EmbmsStreaming"; } + public final class ModemActivityInfo implements android.os.Parcelable { + method public int describeContents(); + method @NonNull public android.telephony.ModemActivityInfo getDelta(@NonNull android.telephony.ModemActivityInfo); + method public long getIdleTimeMillis(); + method public static int getNumTxPowerLevels(); + method public long getReceiveTimeMillis(); + method public long getSleepTimeMillis(); + method public long getTimestampMillis(); + method public long getTransmitDurationMillisAtPowerLevel(int); + method @NonNull public android.util.Range<java.lang.Integer> getTransmitPowerRange(int); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ModemActivityInfo> CREATOR; + field public static final int TX_POWER_LEVEL_0 = 0; // 0x0 + field public static final int TX_POWER_LEVEL_1 = 1; // 0x1 + field public static final int TX_POWER_LEVEL_2 = 2; // 0x2 + field public static final int TX_POWER_LEVEL_3 = 3; // 0x3 + field public static final int TX_POWER_LEVEL_4 = 4; // 0x4 + } + public final class NetworkRegistrationInfo implements android.os.Parcelable { method @Nullable public android.telephony.DataSpecificRegistrationInfo getDataSpecificInfo(); method public int getRegistrationState(); @@ -458,6 +477,10 @@ package android.telephony { field public static final int ROAMING_TYPE_UNKNOWN = 1; // 0x1 } + public class SignalStrength implements android.os.Parcelable { + ctor public SignalStrength(@NonNull android.telephony.SignalStrength); + } + public final class SmsCbCmasInfo implements android.os.Parcelable { ctor public SmsCbCmasInfo(int, int, int, int, int, int); method public int describeContents(); diff --git a/telephony/java/android/telephony/AccessNetworkConstants.java b/telephony/java/android/telephony/AccessNetworkConstants.java index 39a754389254..d01297147fdb 100644 --- a/telephony/java/android/telephony/AccessNetworkConstants.java +++ b/telephony/java/android/telephony/AccessNetworkConstants.java @@ -18,7 +18,6 @@ package android.telephony; import android.annotation.IntDef; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.hardware.radio.V1_1.GeranBands; import android.hardware.radio.V1_5.AccessNetwork; import android.hardware.radio.V1_5.EutranBands; @@ -49,7 +48,6 @@ public final class AccessNetworkConstants { * @hide */ @SystemApi - @TestApi public static final int TRANSPORT_TYPE_INVALID = -1; /** @@ -438,7 +436,6 @@ public final class AccessNetworkConstants { * @hide */ @SystemApi - @TestApi public static final int FREQUENCY_RANGE_GROUP_UNKNOWN = 0; /** @@ -447,7 +444,6 @@ public final class AccessNetworkConstants { * @hide */ @SystemApi - @TestApi public static final int FREQUENCY_RANGE_GROUP_1 = 1; /** @@ -456,7 +452,6 @@ public final class AccessNetworkConstants { * @hide */ @SystemApi - @TestApi public static final int FREQUENCY_RANGE_GROUP_2 = 2; /** @@ -481,7 +476,6 @@ public final class AccessNetworkConstants { * @hide */ @SystemApi - @TestApi public static @FrequencyRangeGroup int getFrequencyRangeGroup(@NgranBand int band) { switch (band) { case BAND_1: diff --git a/telephony/java/android/telephony/BarringInfo.java b/telephony/java/android/telephony/BarringInfo.java index 92423a2f2218..e9698adc0356 100644 --- a/telephony/java/android/telephony/BarringInfo.java +++ b/telephony/java/android/telephony/BarringInfo.java @@ -252,7 +252,6 @@ public final class BarringInfo implements Parcelable { private SparseArray<BarringServiceInfo> mBarringServiceInfos; /** @hide */ - @TestApi @SystemApi public BarringInfo() { mBarringServiceInfos = new SparseArray<>(); diff --git a/telephony/java/android/telephony/CallQuality.java b/telephony/java/android/telephony/CallQuality.java index 1effeb774484..fa70c33965ed 100644 --- a/telephony/java/android/telephony/CallQuality.java +++ b/telephony/java/android/telephony/CallQuality.java @@ -20,7 +20,6 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.os.Parcel; import android.os.Parcelable; @@ -41,7 +40,6 @@ import java.util.Objects; * @hide */ @SystemApi -@TestApi public final class CallQuality implements Parcelable { // Constants representing the call quality level (see #CallQuality); diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index 8261b53a2c9f..3d33b95d34d2 100755..100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -23,7 +23,6 @@ import android.annotation.RequiresPermission; import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.annotation.SystemService; -import android.annotation.TestApi; import android.compat.annotation.UnsupportedAppUsage; import android.content.ComponentName; import android.content.Context; @@ -4603,7 +4602,6 @@ public class CarrierConfigManager { */ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) @SystemApi - @TestApi public void overrideConfig(int subscriptionId, @Nullable PersistableBundle overrideValues) { overrideConfig(subscriptionId, overrideValues, false); } diff --git a/telephony/java/android/telephony/CellSignalStrength.java b/telephony/java/android/telephony/CellSignalStrength.java index 2e7bde3b3d89..e0896570d3ed 100644 --- a/telephony/java/android/telephony/CellSignalStrength.java +++ b/telephony/java/android/telephony/CellSignalStrength.java @@ -17,6 +17,7 @@ package android.telephony; import android.annotation.IntRange; +import android.annotation.SystemApi; import android.os.PersistableBundle; /** @@ -155,11 +156,12 @@ public abstract class CellSignalStrength { /** * Returns the number of signal strength levels. - * @return Number of signal strength levels, enforced to be 5 + * @return Number of signal strength levels, currently defined in the HAL as 5. * * @hide */ - public static final int getNumSignalStrengthLevels() { + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) + public static int getNumSignalStrengthLevels() { return NUM_SIGNAL_STRENGTH_BINS; } } diff --git a/telephony/java/android/telephony/DataSpecificRegistrationInfo.java b/telephony/java/android/telephony/DataSpecificRegistrationInfo.java index e91d6fc9d801..597fe8f85cfa 100644 --- a/telephony/java/android/telephony/DataSpecificRegistrationInfo.java +++ b/telephony/java/android/telephony/DataSpecificRegistrationInfo.java @@ -19,7 +19,6 @@ package android.telephony; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.os.Parcel; import android.os.Parcelable; @@ -31,7 +30,6 @@ import java.util.Objects; * @hide */ @SystemApi -@TestApi public final class DataSpecificRegistrationInfo implements Parcelable { /** * @hide diff --git a/telephony/java/android/telephony/LteVopsSupportInfo.java b/telephony/java/android/telephony/LteVopsSupportInfo.java index 7994c1b05977..83e41bf3df3b 100644 --- a/telephony/java/android/telephony/LteVopsSupportInfo.java +++ b/telephony/java/android/telephony/LteVopsSupportInfo.java @@ -20,7 +20,6 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.os.Parcel; import android.os.Parcelable; @@ -33,7 +32,6 @@ import java.util.Objects; * @hide */ @SystemApi -@TestApi public final class LteVopsSupportInfo implements Parcelable { /**@hide*/ diff --git a/telephony/java/android/telephony/ModemActivityInfo.java b/telephony/java/android/telephony/ModemActivityInfo.java index debb119c94bc..e164c4bc2491 100644 --- a/telephony/java/android/telephony/ModemActivityInfo.java +++ b/telephony/java/android/telephony/ModemActivityInfo.java @@ -16,8 +16,12 @@ package android.telephony; +import android.annotation.DurationMillisLong; +import android.annotation.ElapsedRealtimeLong; import android.annotation.IntDef; import android.annotation.NonNull; +import android.annotation.SystemApi; +import android.annotation.TestApi; import android.os.Parcel; import android.os.Parcelable; import android.os.SystemClock; @@ -25,46 +29,49 @@ import android.util.Range; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; -import java.util.ArrayList; -import java.util.List; +import java.util.Arrays; +import java.util.Objects; /** - * Reports modem activity information. + * Contains information about the modem's activity. May be useful for power stats reporting. * @hide */ +@SystemApi public final class ModemActivityInfo implements Parcelable { + private static final int TX_POWER_LEVELS = 5; + /** - * Tx(transmit) power level. see power index below - * <ul> - * <li> index 0 = tx_power < 0dBm. </li> - * <li> index 1 = 0dBm < tx_power < 5dBm. </li> - * <li> index 2 = 5dBm < tx_power < 15dBm. </li> - * <li> index 3 = 15dBm < tx_power < 20dBm. </li> - * <li> index 4 = tx_power > 20dBm. </li> - * </ul> - */ - public static final int TX_POWER_LEVELS = 5; - /** - * Tx(transmit) power level 0: tx_power < 0dBm + * Corresponds to transmit power of less than 0dBm. */ public static final int TX_POWER_LEVEL_0 = 0; + /** - * Tx(transmit) power level 1: 0dBm < tx_power < 5dBm + * Corresponds to transmit power between 0dBm and 5dBm. */ public static final int TX_POWER_LEVEL_1 = 1; + /** - * Tx(transmit) power level 2: 5dBm < tx_power < 15dBm + * Corresponds to transmit power between 5dBm and 15dBm. */ public static final int TX_POWER_LEVEL_2 = 2; + /** - * Tx(transmit) power level 3: 15dBm < tx_power < 20dBm. + * Corresponds to transmit power between 15dBm and 20dBm. */ public static final int TX_POWER_LEVEL_3 = 3; + /** - * Tx(transmit) power level 4: tx_power > 20dBm + * Corresponds to transmit power above 20dBm. */ public static final int TX_POWER_LEVEL_4 = 4; + /** + * The number of transmit power levels. Fixed by HAL definition. + */ + public static int getNumTxPowerLevels() { + return TX_POWER_LEVELS; + } + /** @hide */ @IntDef(prefix = {"TX_POWER_LEVEL_"}, value = { TX_POWER_LEVEL_0, @@ -82,34 +89,39 @@ public final class ModemActivityInfo implements Parcelable { new Range<>(5, 15), new Range<>(15, 20), new Range<>(20, Integer.MAX_VALUE) - }; private long mTimestamp; private int mSleepTimeMs; private int mIdleTimeMs; - private List<TransmitPower> mTransmitPowerInfo = new ArrayList<>(TX_POWER_LEVELS); + private int[] mTxTimeMs; private int mRxTimeMs; + /** + * @hide + */ + @TestApi public ModemActivityInfo(long timestamp, int sleepTimeMs, int idleTimeMs, @NonNull int[] txTimeMs, int rxTimeMs) { + Objects.requireNonNull(txTimeMs); + if (txTimeMs.length != TX_POWER_LEVELS) { + throw new IllegalArgumentException("txTimeMs must have length == TX_POWER_LEVELS"); + } mTimestamp = timestamp; mSleepTimeMs = sleepTimeMs; mIdleTimeMs = idleTimeMs; - populateTransmitPowerRange(txTimeMs); + mTxTimeMs = txTimeMs; mRxTimeMs = rxTimeMs; } - /** helper API to populate tx power range for each bucket **/ - private void populateTransmitPowerRange(@NonNull int[] transmitPowerMs) { - int i = 0; - for ( ; i < Math.min(transmitPowerMs.length, TX_POWER_LEVELS); i++) { - mTransmitPowerInfo.add(i, new TransmitPower(TX_POWER_RANGES[i], transmitPowerMs[i])); - } - // Make sure that mTransmitPowerInfo is fully initialized. - for ( ; i < TX_POWER_LEVELS; i++) { - mTransmitPowerInfo.add(i, new TransmitPower(TX_POWER_RANGES[i], 0)); - } + /** + * Provided for convenience in manipulation since the API exposes long values but internal + * representations are ints. + * @hide + */ + public ModemActivityInfo(long timestamp, long sleepTimeMs, long idleTimeMs, + @NonNull int[] txTimeMs, long rxTimeMs) { + this(timestamp, (int) sleepTimeMs, (int) idleTimeMs, txTimeMs, (int) rxTimeMs); } @Override @@ -118,7 +130,7 @@ public final class ModemActivityInfo implements Parcelable { + " mTimestamp=" + mTimestamp + " mSleepTimeMs=" + mSleepTimeMs + " mIdleTimeMs=" + mIdleTimeMs - + " mTransmitPowerInfo[]=" + mTransmitPowerInfo.toString() + + " mTxTimeMs[]=" + mTxTimeMs + " mRxTimeMs=" + mRxTimeMs + "}"; } @@ -129,14 +141,12 @@ public final class ModemActivityInfo implements Parcelable { public static final @android.annotation.NonNull Parcelable.Creator<ModemActivityInfo> CREATOR = new Parcelable.Creator<ModemActivityInfo>() { - public ModemActivityInfo createFromParcel(Parcel in) { + public ModemActivityInfo createFromParcel(@NonNull Parcel in) { long timestamp = in.readLong(); int sleepTimeMs = in.readInt(); int idleTimeMs = in.readInt(); int[] txTimeMs = new int[TX_POWER_LEVELS]; - for (int i = 0; i < TX_POWER_LEVELS; i++) { - txTimeMs[i] = in.readInt(); - } + in.readIntArray(txTimeMs); int rxTimeMs = in.readInt(); return new ModemActivityInfo(timestamp, sleepTimeMs, idleTimeMs, txTimeMs, rxTimeMs); @@ -147,21 +157,25 @@ public final class ModemActivityInfo implements Parcelable { } }; - public void writeToParcel(Parcel dest, int flags) { + /** + * @param dest The Parcel in which the object should be written. + * @param flags Additional flags about how the object should be written. + */ + public void writeToParcel(@NonNull Parcel dest, int flags) { dest.writeLong(mTimestamp); dest.writeInt(mSleepTimeMs); dest.writeInt(mIdleTimeMs); - for (int i = 0; i < TX_POWER_LEVELS; i++) { - dest.writeInt(mTransmitPowerInfo.get(i).getTimeInMillis()); - } + dest.writeIntArray(mTxTimeMs); dest.writeInt(mRxTimeMs); } /** - * @return milliseconds since boot, including mTimeInMillis spent in sleep. - * @see SystemClock#elapsedRealtime() + * Gets the timestamp at which this modem activity info was recorded. + * + * @return The timestamp, as returned by {@link SystemClock#elapsedRealtime()}, when this + * {@link ModemActivityInfo} was recorded. */ - public long getTimestamp() { + public @ElapsedRealtimeLong long getTimestampMillis() { return mTimestamp; } @@ -171,35 +185,48 @@ public final class ModemActivityInfo implements Parcelable { } /** - * @return an arrayList of {@link TransmitPower} with each element representing the total time where - * transmitter is awake time (in ms) for a given power range (in dbm). + * Gets the amount of time the modem spent transmitting at a certain power level. * - * @see #TX_POWER_LEVELS + * @param powerLevel The power level to query. + * @return The amount of time, in milliseconds, that the modem spent transmitting at the + * given power level. */ - @NonNull - public List<TransmitPower> getTransmitPowerInfo() { - return mTransmitPowerInfo; + public @DurationMillisLong long getTransmitDurationMillisAtPowerLevel( + @TxPowerLevel int powerLevel) { + return mTxTimeMs[powerLevel]; + } + + /** + * Gets the range of transmit powers corresponding to a certain power level. + * + * @param powerLevel The power level to query + * @return A {@link Range} object representing the range of intensities (in dBm) to which this + * power level corresponds. + */ + public @NonNull Range<Integer> getTransmitPowerRange(@TxPowerLevel int powerLevel) { + return TX_POWER_RANGES[powerLevel]; } /** @hide */ public void setTransmitTimeMillis(int[] txTimeMs) { - populateTransmitPowerRange(txTimeMs); + mTxTimeMs = Arrays.copyOf(txTimeMs, TX_POWER_LEVELS); } - /** @hide */ + /** + * @return The raw array of transmit power durations + * @hide + */ @NonNull public int[] getTransmitTimeMillis() { - int[] transmitTimeMillis = new int[TX_POWER_LEVELS]; - for (int i = 0; i < transmitTimeMillis.length; i++) { - transmitTimeMillis[i] = mTransmitPowerInfo.get(i).getTimeInMillis(); - } - return transmitTimeMillis; + return mTxTimeMs; } /** - * @return total mTimeInMillis (in ms) when modem is in a low power or sleep state. + * Gets the amount of time (in milliseconds) when the modem is in a low power or sleep state. + * + * @return Time in milliseconds. */ - public int getSleepTimeMillis() { + public @DurationMillisLong long getSleepTimeMillis() { return mSleepTimeMs; } @@ -209,10 +236,44 @@ public final class ModemActivityInfo implements Parcelable { } /** - * @return total mTimeInMillis (in ms) when modem is awake but neither the transmitter nor receiver are - * active. + * Provided for convenience, since the API surface needs to return longs but internal + * representations are ints. + * @hide */ - public int getIdleTimeMillis() { + public void setSleepTimeMillis(long sleepTimeMillis) { + mSleepTimeMs = (int) sleepTimeMillis; + } + + /** + * Computes the difference between this instance of {@link ModemActivityInfo} and another + * instance. + * + * This method should be used to compute the amount of activity that has happened between two + * samples of modem activity taken at separate times. The sample passed in as an argument to + * this method should be the one that's taken later in time (and therefore has more activity). + * @param other The other instance of {@link ModemActivityInfo} to diff against. + * @return An instance of {@link ModemActivityInfo} representing the difference in modem + * activity. + */ + public @NonNull ModemActivityInfo getDelta(@NonNull ModemActivityInfo other) { + int[] txTimeMs = new int[ModemActivityInfo.TX_POWER_LEVELS]; + for (int i = 0; i < ModemActivityInfo.TX_POWER_LEVELS; i++) { + txTimeMs[i] = other.mTxTimeMs[i] - mTxTimeMs[i]; + } + return new ModemActivityInfo(other.getTimestampMillis(), + other.getSleepTimeMillis() - getSleepTimeMillis(), + other.getIdleTimeMillis() - getIdleTimeMillis(), + txTimeMs, + other.getReceiveTimeMillis() - getReceiveTimeMillis()); + } + + /** + * Gets the amount of time (in milliseconds) when the modem is awake but neither transmitting + * nor receiving. + * + * @return Time in milliseconds. + */ + public @DurationMillisLong long getIdleTimeMillis() { return mIdleTimeMs; } @@ -222,9 +283,20 @@ public final class ModemActivityInfo implements Parcelable { } /** - * @return rx(receive) mTimeInMillis in ms. + * Provided for convenience, since the API surface needs to return longs but internal + * representations are ints. + * @hide + */ + public void setIdleTimeMillis(long idleTimeMillis) { + mIdleTimeMs = (int) idleTimeMillis; + } + + /** + * Gets the amount of time (in milliseconds) when the modem is awake and receiving data. + * + * @return Time in milliseconds. */ - public int getReceiveTimeMillis() { + public @DurationMillisLong long getReceiveTimeMillis() { return mRxTimeMs; } @@ -234,71 +306,56 @@ public final class ModemActivityInfo implements Parcelable { } /** + * Provided for convenience, since the API surface needs to return longs but internal + * representations are ints. + * @hide + */ + public void setReceiveTimeMillis(long receiveTimeMillis) { + mRxTimeMs = (int) receiveTimeMillis; + } + + /** * Indicates if the modem has reported valid {@link ModemActivityInfo}. * * @return {@code true} if this {@link ModemActivityInfo} record is valid, * {@code false} otherwise. + * TODO: remove usages of this outside Telephony by always returning a valid (or null) result + * from telephony. + * @hide */ + @TestApi public boolean isValid() { - for (TransmitPower powerInfo : getTransmitPowerInfo()) { - if(powerInfo.getTimeInMillis() < 0) { - return false; - } - } + boolean isTxPowerValid = Arrays.stream(mTxTimeMs).allMatch((i) -> i >= 0); - return ((getIdleTimeMillis() >= 0) && (getSleepTimeMillis() >= 0) + return isTxPowerValid && ((getIdleTimeMillis() >= 0) && (getSleepTimeMillis() >= 0) && (getReceiveTimeMillis() >= 0) && !isEmpty()); } private boolean isEmpty() { - for (TransmitPower txVal : getTransmitPowerInfo()) { - if(txVal.getTimeInMillis() != 0) { - return false; - } - } + boolean isTxPowerEmpty = mTxTimeMs == null || mTxTimeMs.length == 0 + || Arrays.stream(mTxTimeMs).allMatch((i) -> i == 0); - return ((getIdleTimeMillis() == 0) && (getSleepTimeMillis() == 0) + return isTxPowerEmpty && ((getIdleTimeMillis() == 0) && (getSleepTimeMillis() == 0) && (getReceiveTimeMillis() == 0)); } - /** - * Transmit power Information, including the power range in dbm and the total time (in ms) where - * the transmitter is active/awake for this power range. - * e.g, range: 0dbm(lower) ~ 5dbm(upper) - * time: 5ms - */ - public class TransmitPower { - private int mTimeInMillis; - private Range<Integer> mPowerRangeInDbm; - /** @hide */ - public TransmitPower(@NonNull Range<Integer> range, int time) { - this.mTimeInMillis = time; - this.mPowerRangeInDbm = range; - } - - /** - * @return the total time in ms where the transmitter is active/wake for this power range - * {@link #getPowerRangeInDbm()}. - */ - public int getTimeInMillis() { - return mTimeInMillis; - } - /** - * @return the power range in dbm. e.g, range: 0dbm(lower) ~ 5dbm(upper) - */ - @NonNull - public Range<Integer> getPowerRangeInDbm() { - return mPowerRangeInDbm; - } + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ModemActivityInfo that = (ModemActivityInfo) o; + return mTimestamp == that.mTimestamp + && mSleepTimeMs == that.mSleepTimeMs + && mIdleTimeMs == that.mIdleTimeMs + && mRxTimeMs == that.mRxTimeMs + && Arrays.equals(mTxTimeMs, that.mTxTimeMs); + } - @Override - public String toString() { - return "TransmitPower{" - + " mTimeInMillis=" + mTimeInMillis - + " mPowerRangeInDbm={" + mPowerRangeInDbm.getLower() - + "," + mPowerRangeInDbm.getUpper() - + "}}"; - } + @Override + public int hashCode() { + int result = Objects.hash(mTimestamp, mSleepTimeMs, mIdleTimeMs, mRxTimeMs); + result = 31 * result + Arrays.hashCode(mTxTimeMs); + return result; } } diff --git a/telephony/java/android/telephony/NetworkRegistrationInfo.java b/telephony/java/android/telephony/NetworkRegistrationInfo.java index aee1e84ca356..92238420fd32 100644 --- a/telephony/java/android/telephony/NetworkRegistrationInfo.java +++ b/telephony/java/android/telephony/NetworkRegistrationInfo.java @@ -20,7 +20,6 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.os.Parcel; import android.os.Parcelable; import android.telephony.AccessNetworkConstants.TransportType; @@ -71,37 +70,37 @@ public final class NetworkRegistrationInfo implements Parcelable { * Not registered. The device is not currently searching a new operator to register. * @hide */ - @SystemApi @TestApi + @SystemApi public static final int REGISTRATION_STATE_NOT_REGISTERED_OR_SEARCHING = 0; /** * Registered on home network. * @hide */ - @SystemApi @TestApi + @SystemApi public static final int REGISTRATION_STATE_HOME = 1; /** * Not registered. The device is currently searching a new operator to register. * @hide */ - @SystemApi @TestApi + @SystemApi public static final int REGISTRATION_STATE_NOT_REGISTERED_SEARCHING = 2; /** * Registration denied. * @hide */ - @SystemApi @TestApi + @SystemApi public static final int REGISTRATION_STATE_DENIED = 3; /** * Registration state is unknown. * @hide */ - @SystemApi @TestApi + @SystemApi public static final int REGISTRATION_STATE_UNKNOWN = 4; /** * Registered on roaming network. * @hide */ - @SystemApi @TestApi + @SystemApi public static final int REGISTRATION_STATE_ROAMING = 5; /** @hide */ @@ -386,7 +385,7 @@ public final class NetworkRegistrationInfo implements Parcelable { * * @hide */ - @SystemApi @TestApi + @SystemApi public @RegistrationState int getRegistrationState() { return mRegistrationState; } @@ -451,7 +450,7 @@ public final class NetworkRegistrationInfo implements Parcelable { * @return the current network roaming type. * @hide */ - @SystemApi @TestApi + @SystemApi public @ServiceState.RoamingType int getRoamingType() { return mRoamingType; } @@ -460,7 +459,7 @@ public final class NetworkRegistrationInfo implements Parcelable { * @return Whether emergency is enabled. * @hide */ - @SystemApi @TestApi + @SystemApi public boolean isEmergencyEnabled() { return mEmergencyOnly; } /** @@ -498,7 +497,7 @@ public final class NetworkRegistrationInfo implements Parcelable { * 10.5.3.6 for UMTS, 3GPP TS 24.301 9.9.3.9 for LTE, and 3GPP2 A.S0001 6.2.2.44 for CDMA * @hide */ - @SystemApi @TestApi + @SystemApi public int getRejectCause() { return mRejectCause; } @@ -545,7 +544,7 @@ public final class NetworkRegistrationInfo implements Parcelable { * @hide */ @Nullable - @SystemApi @TestApi + @SystemApi public DataSpecificRegistrationInfo getDataSpecificInfo() { return mDataSpecificInfo; } @@ -680,7 +679,7 @@ public final class NetworkRegistrationInfo implements Parcelable { * @hide */ @Override - @SystemApi @TestApi + @SystemApi public void writeToParcel(Parcel dest, int flags) { dest.writeInt(mDomain); dest.writeInt(mTransportType); @@ -772,7 +771,7 @@ public final class NetworkRegistrationInfo implements Parcelable { * </code></pre> * @hide */ - @SystemApi @TestApi + @SystemApi public static final class Builder { @Domain private int mDomain; @@ -877,7 +876,7 @@ public final class NetworkRegistrationInfo implements Parcelable { * @return The same instance of the builder. * @hide */ - @SystemApi @TestApi + @SystemApi public @NonNull Builder setEmergencyOnly(boolean emergencyOnly) { mEmergencyOnly = emergencyOnly; return this; @@ -891,7 +890,7 @@ public final class NetworkRegistrationInfo implements Parcelable { * @return The same instance of the builder. * @hide */ - @SystemApi @TestApi + @SystemApi public @NonNull Builder setAvailableServices( @NonNull @ServiceType List<Integer> availableServices) { mAvailableServices = availableServices; @@ -906,7 +905,7 @@ public final class NetworkRegistrationInfo implements Parcelable { * @return The same instance of the builder. * @hide */ - @SystemApi @TestApi + @SystemApi public @NonNull Builder setCellIdentity(@Nullable CellIdentity cellIdentity) { mCellIdentity = cellIdentity; return this; @@ -929,7 +928,7 @@ public final class NetworkRegistrationInfo implements Parcelable { * @return the NetworkRegistrationInfo object. * @hide */ - @SystemApi @TestApi + @SystemApi public @NonNull NetworkRegistrationInfo build() { return new NetworkRegistrationInfo(mDomain, mTransportType, mRegistrationState, mAccessNetworkTechnology, mRejectCause, mEmergencyOnly, mAvailableServices, diff --git a/telephony/java/android/telephony/PhoneNumberUtils.java b/telephony/java/android/telephony/PhoneNumberUtils.java index ec9940836afe..58e368bcc444 100644 --- a/telephony/java/android/telephony/PhoneNumberUtils.java +++ b/telephony/java/android/telephony/PhoneNumberUtils.java @@ -2239,7 +2239,6 @@ public class PhoneNumberUtils { * @hide */ @SystemApi - @TestApi public static boolean isVoiceMailNumber(@NonNull Context context, int subId, @Nullable String number) { String vmNumber, mdn; @@ -2728,7 +2727,6 @@ public class PhoneNumberUtils { * @return true if number contains @ */ @SystemApi - @TestApi public static boolean isUriNumber(@Nullable String number) { // Note we allow either "@" or "%40" to indicate a URI, in case // the passed-in string is URI-escaped. (Neither "@" nor "%40" @@ -2747,7 +2745,6 @@ public class PhoneNumberUtils { * @hide */ @SystemApi - @TestApi public static @NonNull String getUsernameFromUriNumber(@NonNull String number) { // The delimiter between username and domain name can be // either "@" or "%40" (the URI-escaped equivalent.) diff --git a/telephony/java/android/telephony/SignalStrength.java b/telephony/java/android/telephony/SignalStrength.java index 1376cddbc41f..4a0f0076f257 100644 --- a/telephony/java/android/telephony/SignalStrength.java +++ b/telephony/java/android/telephony/SignalStrength.java @@ -18,6 +18,7 @@ package android.telephony; import android.annotation.ElapsedRealtimeLong; import android.annotation.NonNull; +import android.annotation.SystemApi; import android.compat.annotation.UnsupportedAppUsage; import android.os.Build; import android.os.Bundle; @@ -287,12 +288,12 @@ public class SignalStrength implements Parcelable { } /** - * Copy constructors + * This constructor is used to create a copy of an existing SignalStrength object. * * @param s Source SignalStrength - * * @hide */ + @SystemApi public SignalStrength(@NonNull SignalStrength s) { copyFrom(s); } diff --git a/telephony/java/android/telephony/SmsManager.java b/telephony/java/android/telephony/SmsManager.java index cab62098f08e..d3fca3ec78df 100644 --- a/telephony/java/android/telephony/SmsManager.java +++ b/telephony/java/android/telephony/SmsManager.java @@ -2604,13 +2604,12 @@ public final class SmsManager { /** * Send an MMS message * - * <p class="note"><strong>Note:</strong> This method will never trigger an SMS disambiguation - * dialog. If this method is called on a device that has multiple active subscriptions, this - * {@link SmsManager} instance has been created with {@link #getDefault()}, and no user-defined - * default subscription is defined, the subscription ID associated with this message will be - * INVALID, which will result in the operation being completed on the subscription associated - * with logical slot 0. Use {@link #getSmsManagerForSubscriptionId(int)} to ensure the - * operation is performed on the correct subscription. + * <p class="note"><strong>Note:</strong> If {@link #getDefault()} is used to instantiate this + * manager on a multi-SIM device, this operation may fail sending the MMS message because no + * suitable default subscription could be found. In this case, if {@code sentIntent} is + * non-null, then the {@link PendingIntent} will be sent with an error code + * {@code RESULT_NO_DEFAULT_SMS_APP}. See {@link #getDefault()} for more information on the + * conditions where this operation may fail. * </p> * * @param context application context @@ -2629,21 +2628,30 @@ public final class SmsManager { } MmsManager m = (MmsManager) context.getSystemService(Context.MMS_SERVICE); if (m != null) { - m.sendMultimediaMessage(getSubscriptionId(), contentUri, locationUrl, configOverrides, - sentIntent, 0L /* messageId */); + resolveSubscriptionForOperation(new SubscriptionResolverResult() { + @Override + public void onSuccess(int subId) { + m.sendMultimediaMessage(subId, contentUri, locationUrl, configOverrides, + sentIntent, 0L /* messageId */); + } + + @Override + public void onFailure() { + notifySmsError(sentIntent, RESULT_NO_DEFAULT_SMS_APP); + } + }); } } /** * Download an MMS message from carrier by a given location URL * - * <p class="note"><strong>Note:</strong> This method will never trigger an SMS disambiguation - * dialog. If this method is called on a device that has multiple active subscriptions, this - * {@link SmsManager} instance has been created with {@link #getDefault()}, and no user-defined - * default subscription is defined, the subscription ID associated with this message will be - * INVALID, which will result in the operation being completed on the subscription associated - * with logical slot 0. Use {@link #getSmsManagerForSubscriptionId(int)} to ensure the - * operation is performed on the correct subscription. + * <p class="note"><strong>Note:</strong> If {@link #getDefault()} is used to instantiate this + * manager on a multi-SIM device, this operation may fail downloading the MMS message because no + * suitable default subscription could be found. In this case, if {@code downloadedIntent} is + * non-null, then the {@link PendingIntent} will be sent with an error code + * {@code RESULT_NO_DEFAULT_SMS_APP}. See {@link #getDefault()} for more information on the + * conditions where this operation may fail. * </p> * * @param context application context @@ -2666,8 +2674,18 @@ public final class SmsManager { } MmsManager m = (MmsManager) context.getSystemService(Context.MMS_SERVICE); if (m != null) { - m.downloadMultimediaMessage(getSubscriptionId(), locationUrl, contentUri, - configOverrides, downloadedIntent, 0L /* messageId */); + resolveSubscriptionForOperation(new SubscriptionResolverResult() { + @Override + public void onSuccess(int subId) { + m.downloadMultimediaMessage(subId, locationUrl, contentUri, configOverrides, + downloadedIntent, 0L /* messageId */); + } + + @Override + public void onFailure() { + notifySmsError(downloadedIntent, RESULT_NO_DEFAULT_SMS_APP); + } + }); } } diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java index a71a965b1bdb..2e51ef16baf1 100644 --- a/telephony/java/android/telephony/SubscriptionManager.java +++ b/telephony/java/android/telephony/SubscriptionManager.java @@ -32,7 +32,6 @@ import android.annotation.SdkConstant.SdkConstantType; import android.annotation.SuppressAutoDoc; import android.annotation.SystemApi; import android.annotation.SystemService; -import android.annotation.TestApi; import android.app.PendingIntent; import android.app.PropertyInvalidatedCache; import android.compat.annotation.UnsupportedAppUsage; @@ -279,7 +278,6 @@ public class SubscriptionManager { */ @NonNull @SystemApi - @TestApi public static final Uri WFC_ENABLED_CONTENT_URI = Uri.withAppendedPath(CONTENT_URI, "wfc"); /** @@ -299,7 +297,6 @@ public class SubscriptionManager { */ @NonNull @SystemApi - @TestApi public static final Uri ADVANCED_CALLING_ENABLED_CONTENT_URI = Uri.withAppendedPath( CONTENT_URI, "advanced_calling"); @@ -318,7 +315,6 @@ public class SubscriptionManager { */ @NonNull @SystemApi - @TestApi public static final Uri WFC_MODE_CONTENT_URI = Uri.withAppendedPath(CONTENT_URI, "wfc_mode"); /** @@ -336,7 +332,6 @@ public class SubscriptionManager { */ @NonNull @SystemApi - @TestApi public static final Uri WFC_ROAMING_MODE_CONTENT_URI = Uri.withAppendedPath( CONTENT_URI, "wfc_roaming_mode"); @@ -356,7 +351,6 @@ public class SubscriptionManager { */ @NonNull @SystemApi - @TestApi public static final Uri VT_ENABLED_CONTENT_URI = Uri.withAppendedPath( CONTENT_URI, "vt_enabled"); @@ -375,7 +369,6 @@ public class SubscriptionManager { */ @NonNull @SystemApi - @TestApi public static final Uri WFC_ROAMING_ENABLED_CONTENT_URI = Uri.withAppendedPath( CONTENT_URI, "wfc_roaming_enabled"); @@ -1966,7 +1959,6 @@ public class SubscriptionManager { * @hide */ @SystemApi - @TestApi @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) public void setDefaultVoiceSubscriptionId(int subscriptionId) { if (VDBG) logd("setDefaultVoiceSubId sub id = " + subscriptionId); diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index 4f4a133f08ac..9b4d31a51d53 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -2835,11 +2835,13 @@ public class TelephonyManager { }; /** - * Return a collection of all network types - * @return network types + * Returns an array of all valid network types. + * + * @return An integer array containing all valid network types in no particular order. * * @hide */ + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) public static @NonNull @NetworkType int[] getAllNetworkTypes() { return NETWORK_TYPES; } @@ -8355,13 +8357,13 @@ public class TelephonyManager { /** * Values used to return status for hasCarrierPrivileges call. */ - /** @hide */ @SystemApi @TestApi + /** @hide */ @SystemApi public static final int CARRIER_PRIVILEGE_STATUS_HAS_ACCESS = 1; - /** @hide */ @SystemApi @TestApi + /** @hide */ @SystemApi public static final int CARRIER_PRIVILEGE_STATUS_NO_ACCESS = 0; - /** @hide */ @SystemApi @TestApi + /** @hide */ @SystemApi public static final int CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED = -1; - /** @hide */ @SystemApi @TestApi + /** @hide */ @SystemApi public static final int CARRIER_PRIVILEGE_STATUS_ERROR_LOADING_RULES = -2; /** @@ -8563,7 +8565,6 @@ public class TelephonyManager { /** @hide */ @SystemApi - @TestApi @SuppressLint("Doclava125") public int checkCarrierPrivilegesForPackage(String pkgName) { try { @@ -8596,7 +8597,6 @@ public class TelephonyManager { /** @hide */ @SystemApi - @TestApi public List<String> getCarrierPackageNamesForIntent(Intent intent) { return getCarrierPackageNamesForIntentAndPhone(intent, getPhoneId()); } @@ -10098,7 +10098,6 @@ public class TelephonyManager { * @hide */ @SystemApi - @TestApi @RequiresPermission(Manifest.permission.INTERACT_ACROSS_USERS) public @Nullable ComponentName getAndUpdateDefaultRespondViaMessageApplication() { return SmsApplication.getDefaultRespondViaMessageApplication(mContext, true); @@ -10111,7 +10110,6 @@ public class TelephonyManager { * @hide */ @SystemApi - @TestApi @RequiresPermission(Manifest.permission.INTERACT_ACROSS_USERS) public @Nullable ComponentName getDefaultRespondViaMessageApplication() { return SmsApplication.getDefaultRespondViaMessageApplication(mContext, false); @@ -11859,7 +11857,6 @@ public class TelephonyManager { * * @hide */ - @TestApi @SystemApi public static final int INVALID_EMERGENCY_NUMBER_DB_VERSION = -1; @@ -11899,7 +11896,6 @@ public class TelephonyManager { */ @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) @SystemApi - @TestApi public void updateOtaEmergencyNumberDbFilePath( @NonNull ParcelFileDescriptor otaParcelFileDescriptor) { try { @@ -11925,7 +11921,6 @@ public class TelephonyManager { */ @RequiresPermission(android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION) @SystemApi - @TestApi public void resetOtaEmergencyNumberDbFilePath() { try { ITelephony telephony = getITelephony(); @@ -12147,7 +12142,6 @@ public class TelephonyManager { * * @hide */ - @TestApi @SystemApi @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getEmergencyNumberDbVersion() { @@ -12875,7 +12869,6 @@ public class TelephonyManager { * @hide */ @SystemApi - @TestApi @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setSystemSelectionChannels(@NonNull List<RadioAccessSpecifier> specifiers, @NonNull @CallbackExecutor Executor executor, @@ -12893,7 +12886,6 @@ public class TelephonyManager { * @hide */ @SystemApi - @TestApi @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setSystemSelectionChannels(@NonNull List<RadioAccessSpecifier> specifiers) { Objects.requireNonNull(specifiers, "Specifiers must not be null."); @@ -13305,7 +13297,6 @@ public class TelephonyManager { * @hide */ @SystemApi - @TestApi public static final int MOBILE_DATA_POLICY_DATA_ON_NON_DEFAULT_DURING_VOICE_CALL = 1; /** @@ -13320,7 +13311,6 @@ public class TelephonyManager { * @hide */ @SystemApi - @TestApi public static final int MOBILE_DATA_POLICY_MMS_ALWAYS_ALLOWED = 2; /** @@ -13344,7 +13334,6 @@ public class TelephonyManager { * @hide */ @SystemApi - @TestApi @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setMobileDataPolicyEnabledStatus(@MobileDataPolicy int policy, boolean enabled) { try { @@ -13368,7 +13357,6 @@ public class TelephonyManager { * @hide */ @SystemApi - @TestApi @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isMobileDataPolicyEnabled(@MobileDataPolicy int policy) { try { diff --git a/telephony/java/android/telephony/data/DataCallResponse.java b/telephony/java/android/telephony/data/DataCallResponse.java index 579200e15cca..41381c59482b 100644 --- a/telephony/java/android/telephony/data/DataCallResponse.java +++ b/telephony/java/android/telephony/data/DataCallResponse.java @@ -411,6 +411,25 @@ public final class DataCallResponse implements Parcelable { }; /** + * Convert handover failure mode to string. + * + * @param handoverFailureMode Handover failure mode + * @return Handover failure mode in string + * + * @hide + */ + public static String failureModeToString(@HandoverFailureMode int handoverFailureMode) { + switch (handoverFailureMode) { + case HANDOVER_FAILURE_MODE_UNKNOWN: return "unknown"; + case HANDOVER_FAILURE_MODE_LEGACY: return "legacy"; + case HANDOVER_FAILURE_MODE_DO_FALLBACK: return "fallback"; + case HANDOVER_FAILURE_MODE_NO_FALLBACK_RETRY_HANDOVER: return "retry handover"; + case HANDOVER_FAILURE_MODE_NO_FALLBACK_RETRY_SETUP_NORMAL: return "retry setup new one"; + default: return Integer.toString(handoverFailureMode); + } + } + + /** * Provides a convenient way to set the fields of a {@link DataCallResponse} when creating a new * instance. * diff --git a/telephony/java/android/telephony/ims/ImsCallForwardInfo.java b/telephony/java/android/telephony/ims/ImsCallForwardInfo.java index d53a2e6591a2..3f9c8d26ca91 100644 --- a/telephony/java/android/telephony/ims/ImsCallForwardInfo.java +++ b/telephony/java/android/telephony/ims/ImsCallForwardInfo.java @@ -19,7 +19,6 @@ package android.telephony.ims; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.compat.annotation.UnsupportedAppUsage; import android.os.Parcel; import android.os.Parcelable; @@ -33,7 +32,6 @@ import java.lang.annotation.RetentionPolicy; * @hide */ @SystemApi -@TestApi public final class ImsCallForwardInfo implements Parcelable { /** diff --git a/telephony/java/android/telephony/ims/ImsCallProfile.java b/telephony/java/android/telephony/ims/ImsCallProfile.java index f31fcf4050ea..47a0ab61f970 100644 --- a/telephony/java/android/telephony/ims/ImsCallProfile.java +++ b/telephony/java/android/telephony/ims/ImsCallProfile.java @@ -48,7 +48,6 @@ import java.util.List; * @hide */ @SystemApi -@TestApi public final class ImsCallProfile implements Parcelable { private static final String TAG = "ImsCallProfile"; diff --git a/telephony/java/android/telephony/ims/ImsCallSessionListener.java b/telephony/java/android/telephony/ims/ImsCallSessionListener.java index d21a05103241..2fdd195bbb26 100644 --- a/telephony/java/android/telephony/ims/ImsCallSessionListener.java +++ b/telephony/java/android/telephony/ims/ImsCallSessionListener.java @@ -19,7 +19,6 @@ package android.telephony.ims; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.os.RemoteException; import android.telephony.Annotation; import android.telephony.CallQuality; @@ -40,7 +39,6 @@ import com.android.ims.internal.IImsCallSession; // TODO: APIs in here do not conform to API guidelines yet. This can be changed if // ImsCallSessionListenerConverter is also changed. @SystemApi -@TestApi public class ImsCallSessionListener { private final IImsCallSessionListener mListener; diff --git a/telephony/java/android/telephony/ims/ImsConferenceState.java b/telephony/java/android/telephony/ims/ImsConferenceState.java index 9bf2f44395c4..1fa5f52968e5 100644 --- a/telephony/java/android/telephony/ims/ImsConferenceState.java +++ b/telephony/java/android/telephony/ims/ImsConferenceState.java @@ -18,7 +18,6 @@ package android.telephony.ims; import android.annotation.NonNull; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; @@ -38,7 +37,6 @@ import java.util.Set; * @hide */ @SystemApi -@TestApi public final class ImsConferenceState implements Parcelable { private static final String TAG = "ImsConferenceState"; /** diff --git a/telephony/java/android/telephony/ims/ImsException.java b/telephony/java/android/telephony/ims/ImsException.java index 1c3d58d98b4a..50fb828ea217 100644 --- a/telephony/java/android/telephony/ims/ImsException.java +++ b/telephony/java/android/telephony/ims/ImsException.java @@ -19,7 +19,6 @@ package android.telephony.ims; import android.annotation.IntDef; import android.annotation.Nullable; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.content.pm.PackageManager; import android.telephony.SubscriptionManager; import android.text.TextUtils; @@ -83,7 +82,6 @@ public final class ImsException extends Exception { * @hide */ @SystemApi - @TestApi public ImsException(@Nullable String message) { super(getMessage(message, CODE_ERROR_UNSPECIFIED)); } @@ -94,7 +92,6 @@ public final class ImsException extends Exception { * @hide */ @SystemApi - @TestApi public ImsException(@Nullable String message, @ImsErrorCode int code) { super(getMessage(message, code)); mCode = code; @@ -108,7 +105,6 @@ public final class ImsException extends Exception { * @hide */ @SystemApi - @TestApi public ImsException(@Nullable String message, @ImsErrorCode int code, @Nullable Throwable cause) { super(getMessage(message, code), cause); diff --git a/telephony/java/android/telephony/ims/ImsExternalCallState.java b/telephony/java/android/telephony/ims/ImsExternalCallState.java index 7d73165d4540..fdf636c323b6 100644 --- a/telephony/java/android/telephony/ims/ImsExternalCallState.java +++ b/telephony/java/android/telephony/ims/ImsExternalCallState.java @@ -20,7 +20,6 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.net.Uri; import android.os.Parcel; import android.os.Parcelable; @@ -35,7 +34,6 @@ import java.lang.annotation.RetentionPolicy; * @hide */ @SystemApi -@TestApi public final class ImsExternalCallState implements Parcelable { private static final String TAG = "ImsExternalCallState"; diff --git a/telephony/java/android/telephony/ims/ImsMmTelManager.java b/telephony/java/android/telephony/ims/ImsMmTelManager.java index 3a0e49e204cb..76c1fafe05fb 100644 --- a/telephony/java/android/telephony/ims/ImsMmTelManager.java +++ b/telephony/java/android/telephony/ims/ImsMmTelManager.java @@ -25,7 +25,6 @@ import android.annotation.RequiresPermission; import android.annotation.SuppressAutoDoc; import android.annotation.SuppressLint; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.os.Binder; import android.os.RemoteException; import android.os.ServiceSpecificException; @@ -97,7 +96,7 @@ public class ImsMmTelManager implements RegistrationManager { */ // Do not add to this class, add to RegistrationManager.RegistrationCallback instead. @Deprecated - @SystemApi @TestApi + @SystemApi public static class RegistrationCallback extends RegistrationManager.RegistrationCallback { /** @@ -231,7 +230,6 @@ public class ImsMmTelManager implements RegistrationManager { * @hide */ @SystemApi - @TestApi @Deprecated @SuppressAutoDoc // No support for device / profile owner or carrier privileges (b/72967236). @RequiresPermission(anyOf = { @@ -280,7 +278,7 @@ public class ImsMmTelManager implements RegistrationManager { * @hide */ @Deprecated - @SystemApi @TestApi + @SystemApi @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void registerImsRegistrationCallback(@NonNull @CallbackExecutor Executor executor, @NonNull RegistrationCallback c) throws ImsException { @@ -366,7 +364,7 @@ public class ImsMmTelManager implements RegistrationManager { * @hide */ @Deprecated - @SystemApi @TestApi + @SystemApi @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void unregisterImsRegistrationCallback(@NonNull RegistrationCallback c) { if (c == null) { @@ -422,7 +420,7 @@ public class ImsMmTelManager implements RegistrationManager { * @hide */ @Override - @SystemApi @TestApi + @SystemApi @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void getRegistrationState(@NonNull @CallbackExecutor Executor executor, @NonNull @ImsRegistrationState Consumer<Integer> stateCallback) { @@ -681,7 +679,7 @@ public class ImsMmTelManager implements RegistrationManager { * @hide */ @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) - @SystemApi @TestApi + @SystemApi public void setAdvancedCallingSettingEnabled(boolean isEnabled) { ITelephony iTelephony = getITelephony(); if (iTelephony == null) { @@ -725,7 +723,7 @@ public class ImsMmTelManager implements RegistrationManager { * @hide */ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) - @SystemApi @TestApi + @SystemApi public boolean isCapable(@MmTelFeature.MmTelCapabilities.MmTelCapability int capability, @ImsRegistrationImplBase.ImsRegistrationTech int imsRegTech) { ITelephony iTelephony = getITelephony(); @@ -758,7 +756,7 @@ public class ImsMmTelManager implements RegistrationManager { * otherwise. * @hide */ - @SystemApi @TestApi + @SystemApi @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isAvailable(@MmTelFeature.MmTelCapabilities.MmTelCapability int capability, @ImsRegistrationImplBase.ImsRegistrationTech int imsRegTech) { @@ -790,7 +788,7 @@ public class ImsMmTelManager implements RegistrationManager { * available. * @hide */ - @SystemApi @TestApi + @SystemApi @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void isSupported(@MmTelFeature.MmTelCapabilities.MmTelCapability int capability, @AccessNetworkConstants.TransportType int transportType, @@ -879,7 +877,7 @@ public class ImsMmTelManager implements RegistrationManager { * @see #isVtSettingEnabled() * @hide */ - @SystemApi @TestApi + @SystemApi @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) public void setVtSettingEnabled(boolean isEnabled) { ITelephony iTelephony = getITelephony(); @@ -954,7 +952,7 @@ public class ImsMmTelManager implements RegistrationManager { * @see #isVoWiFiSettingEnabled() * @hide */ - @SystemApi @TestApi + @SystemApi @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) public void setVoWiFiSettingEnabled(boolean isEnabled) { ITelephony iTelephony = getITelephony(); @@ -1032,7 +1030,7 @@ public class ImsMmTelManager implements RegistrationManager { * @see #isVoWiFiRoamingSettingEnabled() * @hide */ - @SystemApi @TestApi + @SystemApi @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) public void setVoWiFiRoamingSettingEnabled(boolean isEnabled) { ITelephony iTelephony = getITelephony(); @@ -1069,7 +1067,7 @@ public class ImsMmTelManager implements RegistrationManager { * @see #setVoWiFiSettingEnabled(boolean) * @hide */ - @SystemApi @TestApi + @SystemApi @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) public void setVoWiFiNonPersistent(boolean isCapable, int mode) { ITelephony iTelephony = getITelephony(); @@ -1152,7 +1150,7 @@ public class ImsMmTelManager implements RegistrationManager { * @see #getVoWiFiModeSetting() * @hide */ - @SystemApi @TestApi + @SystemApi @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) public void setVoWiFiModeSetting(@WiFiCallingMode int mode) { ITelephony iTelephony = getITelephony(); @@ -1188,7 +1186,7 @@ public class ImsMmTelManager implements RegistrationManager { * @see #setVoWiFiRoamingSettingEnabled(boolean) * @hide */ - @SystemApi @TestApi + @SystemApi @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public @WiFiCallingMode int getVoWiFiRoamingModeSetting() { ITelephony iTelephony = getITelephony(); @@ -1224,7 +1222,7 @@ public class ImsMmTelManager implements RegistrationManager { * @see #getVoWiFiRoamingModeSetting() * @hide */ - @SystemApi @TestApi + @SystemApi @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) public void setVoWiFiRoamingModeSetting(@WiFiCallingMode int mode) { ITelephony iTelephony = getITelephony(); @@ -1258,7 +1256,7 @@ public class ImsMmTelManager implements RegistrationManager { * @param isEnabled if true RTT should be enabled during calls made on this subscription. * @hide */ - @SystemApi @TestApi + @SystemApi @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) public void setRttCapabilitySetting(boolean isEnabled) { ITelephony iTelephony = getITelephony(); @@ -1338,7 +1336,7 @@ public class ImsMmTelManager implements RegistrationManager { * the IMS service is not available. * @hide */ - @SystemApi @TestApi + @SystemApi @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void getFeatureState(@NonNull @CallbackExecutor Executor executor, @NonNull @ImsFeature.ImsState Consumer<Integer> callback) throws ImsException { diff --git a/telephony/java/android/telephony/ims/ImsService.java b/telephony/java/android/telephony/ims/ImsService.java index 8a05bdfc8401..c806d5c8ed3a 100644 --- a/telephony/java/android/telephony/ims/ImsService.java +++ b/telephony/java/android/telephony/ims/ImsService.java @@ -17,8 +17,8 @@ package android.telephony.ims; import android.annotation.LongDef; +import android.annotation.Nullable; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.app.Service; import android.content.Intent; import android.os.IBinder; @@ -30,12 +30,14 @@ import android.telephony.ims.aidl.IImsRcsFeature; import android.telephony.ims.aidl.IImsRegistration; import android.telephony.ims.aidl.IImsServiceController; import android.telephony.ims.aidl.IImsServiceControllerListener; +import android.telephony.ims.aidl.ISipTransport; import android.telephony.ims.feature.ImsFeature; import android.telephony.ims.feature.MmTelFeature; import android.telephony.ims.feature.RcsFeature; import android.telephony.ims.stub.ImsConfigImplBase; import android.telephony.ims.stub.ImsFeatureConfiguration; import android.telephony.ims.stub.ImsRegistrationImplBase; +import android.telephony.ims.stub.SipTransportImplBase; import android.util.Log; import android.util.SparseArray; @@ -98,25 +100,53 @@ import java.util.Map; * @hide */ @SystemApi -@TestApi public class ImsService extends Service { private static final String LOG_TAG = "ImsService"; /** * This ImsService supports the capability to place emergency calls over MMTEL. + * <p> + * Note: This should never be set by {@link #getImsServiceCapabilities()}, as whether it is + * there or not depends on whether or not {@link ImsFeature#FEATURE_EMERGENCY_MMTEL} is defined + * for this ImsService. If it is set, it will be removed during sanitization before the final + * capabilities bitfield is sent back to the framework. * @hide This is encoded into the {@link ImsFeature#FEATURE_EMERGENCY_MMTEL}, but we will be * adding other capabilities in a central location, so track this capability here as well. */ public static final long CAPABILITY_EMERGENCY_OVER_MMTEL = 1 << 0; /** + * This ImsService supports the capability to create SIP delegates for other IMS applications + * to use to proxy SIP messaging traffic through it. + * <p> + * In order for the framework to report SipDelegate creation as being available for this + * ImsService implementation, this ImsService must report this capability flag in + * {@link #getImsServiceCapabilities()}, {@link #getSipTransport(int)} must not return null, and + * this ImsService MUST report the ability to create both {@link ImsFeature#FEATURE_MMTEL} and + * {@link ImsFeature#FEATURE_RCS} features. + * @hide + */ + public static final long CAPABILITY_SIP_DELEGATE_CREATION = 1 << 1; + + /** + * Used for internal correctness checks of capabilities set by the ImsService implementation and + * tracks the index of the largest defined flag in the capabilities long. + * @hide + */ + public static final long CAPABILITY_MAX_INDEX = + Long.numberOfTrailingZeros(CAPABILITY_SIP_DELEGATE_CREATION); + + /** * @hide */ @LongDef(flag = true, prefix = "CAPABILITY_", value = { - CAPABILITY_EMERGENCY_OVER_MMTEL + // CAPABILITY_EMERGENCY_OVER_MMTEL is not included here because it is managed by + // whether or not ImsFeature.FEATURE_EMERGENCY_MMTEL feature is set and should + // not be set by users of ImsService. + CAPABILITY_SIP_DELEGATE_CREATION }) @Retention(RetentionPolicy.SOURCE) public @interface ImsServiceCapability {} @@ -127,6 +157,7 @@ public class ImsService extends Service { */ private static final Map<Long, String> CAPABILITIES_LOG_MAP = new HashMap<Long, String>() {{ put(CAPABILITY_EMERGENCY_OVER_MMTEL, "EMERGENCY_OVER_MMTEL"); + put(CAPABILITY_SIP_DELEGATE_CREATION, "SIP_DELEGATE_CREATION"); }}; /** @@ -201,6 +232,17 @@ public class ImsService extends Service { } @Override + public long getImsServiceCapabilities() { + long caps = ImsService.this.getImsServiceCapabilities(); + long sanitizedCaps = sanitizeCapabilities(caps); + if (caps != sanitizedCaps) { + Log.w(LOG_TAG, "removing invalid bits from field: 0x" + + Long.toHexString(caps ^ sanitizedCaps)); + } + return sanitizedCaps; + } + + @Override public void notifyImsServiceReadyForFeatureCreation() { ImsService.this.readyForFeatureCreation(); } @@ -218,6 +260,12 @@ public class ImsService extends Service { } @Override + public ISipTransport getSipTransport(int slotId) { + SipTransportImplBase s = ImsService.this.getSipTransport(slotId); + return s != null ? s.getBinder() : null; + } + + @Override public void enableIms(int slotId) { ImsService.this.enableIms(slotId); } @@ -371,6 +419,17 @@ public class ImsService extends Service { } /** + * The optional capabilities that this ImsService supports. + * <p> + * This should be a static configuration and should not change at runtime. + * @return The optional static capabilities of this ImsService implementation. + * @hide + */ + public @ImsServiceCapability long getImsServiceCapabilities() { + return 0L; + } + + /** * The ImsService has been bound and is ready for ImsFeature creation based on the Features that * the ImsService has registered for with the framework, either in the manifest or via * {@link #querySupportedImsFeatures()}. @@ -443,7 +502,37 @@ public class ImsService extends Service { } /** - * @return A string representation of the ImsService capabilties for logging. + * Return the {@link SipTransportImplBase} implementation associated with the provided slot. + * <p> + * This is an optional interface used for devices that must support IMS single registration and + * proxy SIP traffic to remote IMS applications. If this is not supported for this IMS service, + * this method should return {@code null}. If this feature is supported, then this method must + * never be {@code null} and the optional ImsService capability flag + * {@link #CAPABILITY_SIP_DELEGATE_CREATION} must be set in + * {@link #getImsServiceCapabilities()}. Otherwise the framework will assume this feature is not + * supported for this ImsService. + * @param slotId The slot that is associated with the SipTransport implementation. + * @return the SipTransport implementation for the specified slot. + * @hide Keep this hidden until there is something to expose in SipTransport. + */ + public @Nullable SipTransportImplBase getSipTransport(int slotId) { + return null; + } + + private static long sanitizeCapabilities(@ImsServiceCapability long caps) { + long filter = 0xFFFFFFFFFFFFFFFFL; + // pad the "allowed" set with zeros + filter <<= CAPABILITY_MAX_INDEX + 1; + // remove values above the allowed set. + caps &= ~filter; + // CAPABILITY_EMERGENCY_OVER_MMTEL should also not be set here, will be set by telephony + // internally. + caps &= ~CAPABILITY_EMERGENCY_OVER_MMTEL; + return caps; + } + + /** + * @return A string representation of the ImsService capabilities for logging. * @hide */ public static String getCapabilitiesString(@ImsServiceCapability long caps) { @@ -467,4 +556,4 @@ public class ImsService extends Service { result.append("}"); return result.toString(); } -}
\ No newline at end of file +} diff --git a/telephony/java/android/telephony/ims/ImsSsData.java b/telephony/java/android/telephony/ims/ImsSsData.java index 70bf0c57366d..fb8e5d37875b 100644 --- a/telephony/java/android/telephony/ims/ImsSsData.java +++ b/telephony/java/android/telephony/ims/ImsSsData.java @@ -19,7 +19,6 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.os.Parcel; import android.os.Parcelable; @@ -37,7 +36,6 @@ import java.util.List; * {@hide} */ @SystemApi -@TestApi public final class ImsSsData implements Parcelable { private static final String TAG = ImsSsData.class.getCanonicalName(); diff --git a/telephony/java/android/telephony/ims/ImsSsInfo.java b/telephony/java/android/telephony/ims/ImsSsInfo.java index 9cce95fc67f8..27b56b8c5b47 100644 --- a/telephony/java/android/telephony/ims/ImsSsInfo.java +++ b/telephony/java/android/telephony/ims/ImsSsInfo.java @@ -20,7 +20,6 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.compat.annotation.UnsupportedAppUsage; import android.os.Parcel; import android.os.Parcelable; @@ -38,7 +37,6 @@ import java.lang.annotation.RetentionPolicy; * @hide */ @SystemApi -@TestApi public final class ImsSsInfo implements Parcelable { /**@hide*/ diff --git a/telephony/java/android/telephony/ims/ImsStreamMediaProfile.java b/telephony/java/android/telephony/ims/ImsStreamMediaProfile.java index b70fd649ab79..131cb1a505fb 100644 --- a/telephony/java/android/telephony/ims/ImsStreamMediaProfile.java +++ b/telephony/java/android/telephony/ims/ImsStreamMediaProfile.java @@ -18,7 +18,6 @@ package android.telephony.ims; import android.annotation.NonNull; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.compat.annotation.UnsupportedAppUsage; import android.os.Parcel; import android.os.Parcelable; @@ -30,7 +29,6 @@ import android.os.Parcelable; * @hide */ @SystemApi -@TestApi public final class ImsStreamMediaProfile implements Parcelable { private static final String TAG = "ImsStreamMediaProfile"; diff --git a/telephony/java/android/telephony/ims/ImsSuppServiceNotification.java b/telephony/java/android/telephony/ims/ImsSuppServiceNotification.java index f67f68e2e425..16303685d0a3 100644 --- a/telephony/java/android/telephony/ims/ImsSuppServiceNotification.java +++ b/telephony/java/android/telephony/ims/ImsSuppServiceNotification.java @@ -19,7 +19,6 @@ package android.telephony.ims; import android.annotation.NonNull; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.os.Parcel; import android.os.Parcelable; @@ -32,7 +31,6 @@ import java.util.Arrays; * @hide */ @SystemApi -@TestApi public final class ImsSuppServiceNotification implements Parcelable { private static final String TAG = "ImsSuppServiceNotification"; diff --git a/telephony/java/android/telephony/ims/ImsUtListener.java b/telephony/java/android/telephony/ims/ImsUtListener.java index 460a032ce7e0..baa0576cdf13 100644 --- a/telephony/java/android/telephony/ims/ImsUtListener.java +++ b/telephony/java/android/telephony/ims/ImsUtListener.java @@ -18,7 +18,6 @@ package android.telephony.ims; import android.annotation.NonNull; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.os.Bundle; import android.os.RemoteException; import android.telephony.ims.stub.ImsUtImplBase; @@ -34,7 +33,6 @@ import com.android.ims.internal.IImsUtListener; // DO NOT remove or change the existing APIs, only add new ones to this Base implementation or you // will break other implementations of ImsUt maintained by other ImsServices. @SystemApi -@TestApi public class ImsUtListener { /** diff --git a/telephony/java/android/telephony/ims/ImsVideoCallProvider.java b/telephony/java/android/telephony/ims/ImsVideoCallProvider.java index 569c6d5a4e4d..2fca4096f447 100644 --- a/telephony/java/android/telephony/ims/ImsVideoCallProvider.java +++ b/telephony/java/android/telephony/ims/ImsVideoCallProvider.java @@ -17,14 +17,12 @@ package android.telephony.ims; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.compat.annotation.UnsupportedAppUsage; import android.net.Uri; import android.os.Handler; import android.os.Looper; import android.os.Message; import android.os.RemoteException; -import android.telecom.Connection; import android.telecom.VideoProfile; import android.telecom.VideoProfile.CameraCapabilities; import android.view.Surface; @@ -37,7 +35,6 @@ import com.android.internal.os.SomeArgs; * @hide */ @SystemApi -@TestApi public abstract class ImsVideoCallProvider { private static final int MSG_SET_CALLBACK = 1; private static final int MSG_SET_CAMERA = 2; diff --git a/telephony/java/android/telephony/ims/ProvisioningManager.java b/telephony/java/android/telephony/ims/ProvisioningManager.java index 3affdf64aae7..24ae979bb08d 100644 --- a/telephony/java/android/telephony/ims/ProvisioningManager.java +++ b/telephony/java/android/telephony/ims/ProvisioningManager.java @@ -23,7 +23,6 @@ import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.StringDef; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.annotation.WorkerThread; import android.os.Binder; import android.os.RemoteException; @@ -59,7 +58,6 @@ import java.util.concurrent.Executor; * @hide */ @SystemApi -@TestApi public class ProvisioningManager { /**@hide*/ diff --git a/telephony/java/android/telephony/ims/RcsContactPresenceTuple.aidl b/telephony/java/android/telephony/ims/RcsContactPresenceTuple.aidl new file mode 100644 index 000000000000..a70470294794 --- /dev/null +++ b/telephony/java/android/telephony/ims/RcsContactPresenceTuple.aidl @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +package android.telephony.ims; + +parcelable RcsContactPresenceTuple; diff --git a/telephony/java/android/telephony/ims/RcsContactPresenceTuple.java b/telephony/java/android/telephony/ims/RcsContactPresenceTuple.java new file mode 100644 index 000000000000..b0aaa92dd0ac --- /dev/null +++ b/telephony/java/android/telephony/ims/RcsContactPresenceTuple.java @@ -0,0 +1,360 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telephony.ims; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.StringDef; +import android.net.Uri; +import android.os.Parcel; +import android.os.Parcelable; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * Represents a PIDF tuple element that is part of the presence element returned from the carrier + * network during a SUBSCRIBE request. See RFC3863 for more information. + * @hide + */ +public class RcsContactPresenceTuple implements Parcelable { + + /** The service id of the MMTEL */ + public static final String SERVICE_ID_MMTEL = "org.3gpp.urn:urn-7:3gpp-service.ims.icsi.mmtel"; + + /** The service capabilities is available. */ + public static final String TUPLE_BASIC_STATUS_OPEN = "open"; + + /** The service capabilities is unavailable. */ + public static final String TUPLE_BASIC_STATUS_CLOSED = "closed"; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @StringDef(prefix = "TUPLE_BASIC_STATUS_", value = { + TUPLE_BASIC_STATUS_OPEN, + TUPLE_BASIC_STATUS_CLOSED + }) + public @interface BasicStatus {} + + /** + * An optional addition to the PIDF Presence Tuple containing service capabilities, which is + * defined in the servcaps element. See RFC5196, section 3.2.1. + */ + public static class ServiceCapabilities implements Parcelable { + + /** The service can simultaneously send and receive data. */ + public static final String DUPLEX_MODE_FULL = "full"; + + /** The service can alternate between sending and receiving data.*/ + public static final String DUPLEX_MODE_HALF = "half"; + + /** The service can only receive data. */ + public static final String DUPLEX_MODE_RECEIVE_ONLY = "receive-only"; + + /** The service can only send data. */ + public static final String DUPLEX_MODE_SEND_ONLY = "send-only"; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @StringDef(prefix = "DUPLEX_MODE_", value = { + DUPLEX_MODE_FULL, + DUPLEX_MODE_HALF, + DUPLEX_MODE_RECEIVE_ONLY, + DUPLEX_MODE_SEND_ONLY + }) + public @interface DuplexMode {} + + /** + * Builder to help construct {@link ServiceCapabilities} instances. + */ + public static class Builder { + + private ServiceCapabilities mCapabilities; + + /** + * Create the ServiceCapabilities builder, which can be used to set service capabilities + * as well as custom capability extensions. + * @param isAudioCapable Whether the audio is capable or not. + * @param isVideoCapable Whether the video is capable or not. + */ + public Builder(boolean isAudioCapable, boolean isVideoCapable) { + mCapabilities = new ServiceCapabilities(isAudioCapable, isVideoCapable); + } + + /** + * Add the supported duplex mode. + * @param mode The supported duplex mode + */ + public Builder addSupportedDuplexMode(@NonNull @DuplexMode String mode) { + mCapabilities.mSupportedDuplexModeList.add(mode); + return this; + } + + /** + * Add the unsupported duplex mode. + * @param mode The unsupported duplex mode + */ + public Builder addUnsupportedDuplexMode(@NonNull @DuplexMode String mode) { + mCapabilities.mUnsupportedDuplexModeList.add(mode); + return this; + } + + /** + * @return the ServiceCapabilities instance. + */ + public ServiceCapabilities build() { + return mCapabilities; + } + } + + private final boolean mIsAudioCapable; + private final boolean mIsVideoCapable; + private final @DuplexMode List<String> mSupportedDuplexModeList = new ArrayList<>(); + private final @DuplexMode List<String> mUnsupportedDuplexModeList = new ArrayList<>(); + + /** + * Use {@link Builder} to build an instance of this interface. + * @param isAudioCapable Whether the audio is capable. + * @param isVideoCapable Whether the video is capable. + */ + ServiceCapabilities(boolean isAudioCapable, boolean isVideoCapable) { + mIsAudioCapable = isAudioCapable; + mIsVideoCapable = isVideoCapable; + } + + private ServiceCapabilities(Parcel in) { + mIsAudioCapable = in.readBoolean(); + mIsVideoCapable = in.readBoolean(); + in.readStringList(mSupportedDuplexModeList); + in.readStringList(mUnsupportedDuplexModeList); + } + @Override + public void writeToParcel(@NonNull Parcel out, int flags) { + out.writeBoolean(mIsAudioCapable); + out.writeBoolean(mIsVideoCapable); + out.writeStringList(mSupportedDuplexModeList); + out.writeStringList(mUnsupportedDuplexModeList); + } + + @Override + public int describeContents() { + return 0; + } + + public static final @NonNull Creator<ServiceCapabilities> CREATOR = + new Creator<ServiceCapabilities>() { + @Override + public ServiceCapabilities createFromParcel(Parcel in) { + return new ServiceCapabilities(in); + } + + @Override + public ServiceCapabilities[] newArray(int size) { + return new ServiceCapabilities[size]; + } + }; + + /** + * Query the audio capable. + * @return true if the audio is capable, false otherwise. + */ + public boolean isAudioCapable() { + return mIsAudioCapable; + } + + /** + * Query the video capable. + * @return true if the video is capable, false otherwise. + */ + public boolean isVideoCapable() { + return mIsVideoCapable; + } + + /** + * Get the supported duplex mode list. + * @return The list of supported duplex mode + */ + public @NonNull @DuplexMode List<String> getSupportedDuplexModes() { + return Collections.unmodifiableList(mSupportedDuplexModeList); + } + + /** + * Get the unsupported duplex mode list. + * @return The list of unsupported duplex mode + */ + public @NonNull @DuplexMode List<String> getUnsupportedDuplexModes() { + return Collections.unmodifiableList(mUnsupportedDuplexModeList); + } + } + + /** + * Builder to help construct {@link RcsContactPresenceTuple} instances. + */ + public static class Builder { + + private RcsContactPresenceTuple mPresenceTuple; + + /** + * Builds a RcsContactPresenceTuple instance. + * @param serviceId The OMA Presence service-id associated with this capability. See the + * OMA Presence SIMPLE specification v1.1, section 10.5.1. + * @param serviceVersion The OMA Presence version associated with the service capability. + * See the OMA Presence SIMPLE specification v1.1, section 10.5.1. + */ + public Builder(@NonNull @BasicStatus String status, @NonNull String serviceId, + @NonNull String serviceVersion) { + mPresenceTuple = new RcsContactPresenceTuple(status, serviceId, serviceVersion); + } + + /** + * The optional SIP Contact URI associated with the PIDF tuple element. + */ + public Builder addContactUri(@NonNull Uri contactUri) { + mPresenceTuple.mContactUri = contactUri; + return this; + } + + /** + * The optional timestamp indicating the data and time of the status change of this tuple. + * See RFC3863, section 4.1.7 for more information on the expected format. + */ + public Builder addTimeStamp(@NonNull String timestamp) { + mPresenceTuple.mTimestamp = timestamp; + return this; + } + + /** + * An optional parameter containing the description element of the service-description. See + * OMA Presence SIMPLE specification v1.1 + */ + public Builder addDescription(@NonNull String description) { + mPresenceTuple.mServiceDescription = description; + return this; + } + + /** + * An optional parameter containing the service capabilities of the presence tuple if they + * are present in the servcaps element. + */ + public Builder addServiceCapabilities(@NonNull ServiceCapabilities caps) { + mPresenceTuple.mServiceCapabilities = caps; + return this; + } + + /** + * @return the constructed instance. + */ + public RcsContactPresenceTuple build() { + return mPresenceTuple; + } + } + + private Uri mContactUri; + private String mTimestamp; + private @BasicStatus String mStatus; + + // The service information in the service-description element. + private String mServiceId; + private String mServiceVersion; + private String mServiceDescription; + + private ServiceCapabilities mServiceCapabilities; + + private RcsContactPresenceTuple(@NonNull @BasicStatus String status, @NonNull String serviceId, + @NonNull String serviceVersion) { + mStatus = status; + mServiceId = serviceId; + mServiceVersion = serviceVersion; + } + + private RcsContactPresenceTuple(Parcel in) { + mContactUri = in.readParcelable(Uri.class.getClassLoader()); + mTimestamp = in.readString(); + mStatus = in.readString(); + mServiceId = in.readString(); + mServiceVersion = in.readString(); + mServiceDescription = in.readString(); + mServiceCapabilities = in.readParcelable(ServiceCapabilities.class.getClassLoader()); + } + + @Override + public void writeToParcel(@NonNull Parcel out, int flags) { + out.writeParcelable(mContactUri, flags); + out.writeString(mTimestamp); + out.writeString(mStatus); + out.writeString(mServiceId); + out.writeString(mServiceVersion); + out.writeString(mServiceDescription); + out.writeParcelable(mServiceCapabilities, flags); + } + + @Override + public int describeContents() { + return 0; + } + + public static final @NonNull Creator<RcsContactPresenceTuple> CREATOR = + new Creator<RcsContactPresenceTuple>() { + @Override + public RcsContactPresenceTuple createFromParcel(Parcel in) { + return new RcsContactPresenceTuple(in); + } + + @Override + public RcsContactPresenceTuple[] newArray(int size) { + return new RcsContactPresenceTuple[size]; + } + }; + + /** @return the status of the tuple element. */ + public @NonNull @BasicStatus String getStatus() { + return mStatus; + } + + /** @return the service-id element of the service-description */ + public @NonNull String getServiceId() { + return mServiceId; + } + + /** @return the version element of the service-description */ + public @NonNull String getServiceVersion() { + return mServiceVersion; + } + + /** @return the SIP URI contained in the contact element of the tuple if it exists. */ + public @Nullable Uri getContactUri() { + return mContactUri; + } + + /** @return the timestamp element contained in the tuple if it exists */ + public @Nullable String getTimestamp() { + return mTimestamp; + } + + /** @return the description element contained in the service-description if it exists */ + public @Nullable String getServiceDescription() { + return mServiceDescription; + } + + /** @return the {@link ServiceCapabilities} of the tuple if it exists. */ + public @Nullable ServiceCapabilities getServiceCapabilities() { + return mServiceCapabilities; + } +} diff --git a/telephony/java/android/telephony/ims/RcsContactUceCapability.java b/telephony/java/android/telephony/ims/RcsContactUceCapability.java index dc36edf5aad9..d12a6aef5186 100644 --- a/telephony/java/android/telephony/ims/RcsContactUceCapability.java +++ b/telephony/java/android/telephony/ims/RcsContactUceCapability.java @@ -16,7 +16,7 @@ package android.telephony.ims; -import android.annotation.LongDef; +import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.net.Uri; @@ -27,9 +27,7 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.Collections; -import java.util.HashMap; import java.util.List; -import java.util.Map; /** * Contains the User Capability Exchange capabilities corresponding to a contact's URI. @@ -37,114 +35,80 @@ import java.util.Map; */ public final class RcsContactUceCapability implements Parcelable { - /** Supports 1-to-1 chat */ - public static final int CAPABILITY_CHAT_STANDALONE = (1 << 0); - /** Supports group chat */ - public static final int CAPABILITY_CHAT_SESSION = (1 << 1); - /** Supports full store and forward group chat information. */ - public static final int CAPABILITY_CHAT_SESSION_STORE_FORWARD = (1 << 2); + /** Contains presence information associated with the contact */ + public static final int CAPABILITY_MECHANISM_PRESENCE = 1; + + /** Contains OPTIONS information associated with the contact */ + public static final int CAPABILITY_MECHANISM_OPTIONS = 2; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = "CAPABILITY_MECHANISM_", value = { + CAPABILITY_MECHANISM_PRESENCE, + CAPABILITY_MECHANISM_OPTIONS + }) + public @interface CapabilityMechanism {} + /** - * Supports file transfer via Message Session Relay Protocol (MSRP) without Store and Forward. + * The capabilities of this contact were requested recently enough to still be considered in + * the availability window. */ - public static final int CAPABILITY_FILE_TRANSFER = (1 << 3); - /** Supports File Transfer Thumbnail */ - public static final int CAPABILITY_FILE_TRANSFER_THUMBNAIL = (1 << 4); - /** Supports File Transfer with Store and Forward */ - public static final int CAPABILITY_FILE_TRANSFER_STORE_FORWARD = (1 << 5); - /** Supports File Transfer via HTTP */ - public static final int CAPABILITY_FILE_TRANSFER_HTTP = (1 << 6); - /** Supports file transfer via SMS */ - public static final int CAPABILITY_FILE_TRANSFER_SMS = (1 << 7); - /** Supports image sharing */ - public static final int CAPABILITY_IMAGE_SHARE = (1 << 8); - /** Supports video sharing during a circuit-switch call (IR.74)*/ - public static final int CAPABILITY_VIDEO_SHARE_DURING_CS_CALL = (1 << 9); - /** Supports video share outside of voice call (IR.84) */ - public static final int CAPABILITY_VIDEO_SHARE = (1 << 10); - /** Supports social presence information */ - public static final int CAPABILITY_SOCIAL_PRESENCE = (1 << 11); - /** Supports capability discovery via presence */ - public static final int CAPABILITY_DISCOVERY_VIA_PRESENCE = (1 << 12); - /** Supports IP Voice calling over LTE or IWLAN (IR.92/IR.51) */ - public static final int CAPABILITY_IP_VOICE_CALL = (1 << 13); - /** Supports IP video calling (IR.94) */ - public static final int CAPABILITY_IP_VIDEO_CALL = (1 << 14); - /** Supports Geolocation PUSH during 1-to-1 or multiparty chat */ - public static final int CAPABILITY_GEOLOCATION_PUSH = (1 << 15); - /** Supports Geolocation PUSH via SMS for fallback. */ - public static final int CAPABILITY_GEOLOCATION_PUSH_SMS = (1 << 16); - /** Supports Geolocation pull. */ - public static final int CAPABILITY_GEOLOCATION_PULL = (1 << 17); - /** Supports Geolocation pull using file transfer support. */ - public static final int CAPABILITY_GEOLOCATION_PULL_FILE_TRANSFER = (1 << 18); - /** Supports RCS voice calling */ - public static final int CAPABILITY_RCS_VOICE_CALL = (1 << 19); - /** Supports RCS video calling */ - public static final int CAPABILITY_RCS_VIDEO_CALL = (1 << 20); - /** Supports RCS video calling, where video media can not be dropped. */ - public static final int CAPABILITY_RCS_VIDEO_ONLY_CALL = (1 << 21); - /** Supports call composer, where outgoing calls can be enriched with pre-call content.*/ - public static final int CAPABILITY_CALL_COMPOSER = (1 << 22); - /** Supports post call information that is included in the call if the call is missed.*/ - public static final int CAPABILITY_POST_CALL = (1 << 23); - /** Supports sharing a map where the user can draw, share markers, and share their position. */ - public static final int CAPABILITY_SHARED_MAP = (1 << 24); - /** Supports sharing a canvas, where users can draw, add images, and change background colors.*/ - public static final int CAPABILITY_SHARED_SKETCH = (1 << 25); - /** Supports communication with Chatbots. */ - public static final int CAPABILITY_CHAT_BOT = (1 << 26); - /** Supports Chatbot roles. */ - public static final int CAPABILITY_CHAT_BOT_ROLE = (1 << 27); - /** Supports the unidirectional plug-ins framework. */ - public static final int CAPABILITY_PLUG_IN = (1 << 28); - /** Supports standalone Chatbot communication. */ - public static final int CAPABILITY_STANDALONE_CHAT_BOT = (1 << 29); - /** Supports MMTEL based call composer. */ - public static final int CAPABILITY_MMTEL_CALL_COMPOSER = (1 << 30); - - - - /** @hide*/ + public static final int SOURCE_TYPE_NETWORK = 0; + + /** + * The capabilities of this contact were retrieved from the cached information in the Enhanced + * Address Book. + */ + public static final int SOURCE_TYPE_CACHED = 1; + + /** @hide */ @Retention(RetentionPolicy.SOURCE) - @LongDef(prefix = "CAPABILITY_", flag = true, value = { - CAPABILITY_CHAT_STANDALONE, - CAPABILITY_CHAT_SESSION, - CAPABILITY_CHAT_SESSION_STORE_FORWARD, - CAPABILITY_FILE_TRANSFER, - CAPABILITY_FILE_TRANSFER_THUMBNAIL, - CAPABILITY_FILE_TRANSFER_STORE_FORWARD, - CAPABILITY_FILE_TRANSFER_HTTP, - CAPABILITY_FILE_TRANSFER_SMS, - CAPABILITY_IMAGE_SHARE, - CAPABILITY_VIDEO_SHARE_DURING_CS_CALL, - CAPABILITY_VIDEO_SHARE, - CAPABILITY_SOCIAL_PRESENCE, - CAPABILITY_DISCOVERY_VIA_PRESENCE, - CAPABILITY_IP_VOICE_CALL, - CAPABILITY_IP_VIDEO_CALL, - CAPABILITY_GEOLOCATION_PUSH, - CAPABILITY_GEOLOCATION_PUSH_SMS, - CAPABILITY_GEOLOCATION_PULL, - CAPABILITY_GEOLOCATION_PULL_FILE_TRANSFER, - CAPABILITY_RCS_VOICE_CALL, - CAPABILITY_RCS_VIDEO_CALL, - CAPABILITY_RCS_VIDEO_ONLY_CALL, - CAPABILITY_CALL_COMPOSER, - CAPABILITY_POST_CALL, - CAPABILITY_SHARED_MAP, - CAPABILITY_SHARED_SKETCH, - CAPABILITY_CHAT_BOT, - CAPABILITY_CHAT_BOT_ROLE, - CAPABILITY_PLUG_IN, - CAPABILITY_STANDALONE_CHAT_BOT, - CAPABILITY_MMTEL_CALL_COMPOSER + @IntDef(prefix = "SOURCE_TYPE_", value = { + SOURCE_TYPE_NETWORK, + SOURCE_TYPE_CACHED }) - public @interface CapabilityFlag {} + public @interface SourceType {} + + /** + * The requested contact was found to be offline when queried. This is only applicable to + * contact capabilities that were queried via OPTIONS requests and the network returned a + * 408/480 response. + */ + public static final int REQUEST_RESULT_NOT_ONLINE = 0; + + /** + * Capability information for the requested contact was not found. The contact should not be + * considered an RCS user. + */ + public static final int REQUEST_RESULT_NOT_FOUND = 1; /** - * Builder to help construct {@link RcsContactUceCapability} instances. + * Capability information for the requested contact was found successfully. */ - public static class Builder { + public static final int REQUEST_RESULT_FOUND = 2; + + /** + * Capability information for the requested contact has expired and can not be refreshed due to + * a temporary network error. This is a temporary error and the capabilities of the contact + * should be queried again at a later time. + */ + public static final int REQUEST_RESULT_UNKNOWN = 3; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = "REQUEST_RESULT_", value = { + REQUEST_RESULT_NOT_ONLINE, + REQUEST_RESULT_NOT_FOUND, + REQUEST_RESULT_FOUND, + REQUEST_RESULT_UNKNOWN + }) + public @interface RequestResult {} + + /** + * Builder to help construct {@link RcsContactUceCapability} instances when capabilities were + * queried through SIP OPTIONS. + */ + public static class OptionsBuilder { private final RcsContactUceCapability mCapabilities; @@ -153,51 +117,38 @@ public final class RcsContactUceCapability implements Parcelable { * capability extensions. * @param contact The contact URI that the capabilities are attached to. */ - public Builder(@NonNull Uri contact) { - mCapabilities = new RcsContactUceCapability(contact); + public OptionsBuilder(@NonNull Uri contact) { + mCapabilities = new RcsContactUceCapability(contact, CAPABILITY_MECHANISM_OPTIONS, + SOURCE_TYPE_NETWORK); } /** - * Add a UCE capability bit-field as well as the associated URI that the framework should - * use for those services. This is mainly used for capabilities that may use a URI separate - * from the contact's URI, for example the URI to use for VT calls. - * @param type The capability to map to a service URI that is different from the contact's - * URI. + * Set the result of the capabilities request. + * @param requestResult the request result + * @return this OptionBuilder */ - public @NonNull Builder add(@CapabilityFlag long type, @NonNull Uri serviceUri) { - mCapabilities.mCapabilities |= type; - // Put each of these capabilities into the map separately. - for (long shift = 0; shift < Integer.SIZE; shift++) { - long cap = type & (1 << shift); - if (cap != 0) { - mCapabilities.mServiceMap.put(cap, serviceUri); - // remove that capability from the field. - type &= ~cap; - } - if (type == 0) { - // no need to keep going, end early. - break; - } - } + public @NonNull OptionsBuilder setRequestResult(@RequestResult int requestResult) { + mCapabilities.mRequestResult = requestResult; return this; } /** - * Add a UCE capability flag that this contact supports. - * @param type the capability that the contact supports. + * Add the feature tag into the capabilities instance. + * @param tag the supported feature tag + * @return this OptionBuilder */ - public @NonNull Builder add(@CapabilityFlag long type) { - mCapabilities.mCapabilities |= type; + public @NonNull OptionsBuilder addFeatureTag(String tag) { + mCapabilities.mFeatureTags.add(tag); return this; } /** - * Add a carrier specific service tag. - * @param extension A string containing a carrier specific service tag that is an extension - * of the {@link CapabilityFlag}s that are defined here. + * Add the list of feature tag into the capabilities instance. + * @param tags the list of the supported feature tags + * @return this OptionBuilder */ - public @NonNull Builder add(@NonNull String extension) { - mCapabilities.mExtensionTags.add(extension); + public @NonNull OptionsBuilder addFeatureTags(List<String> tags) { + mCapabilities.mFeatureTags.addAll(tags); return this; } @@ -209,56 +160,88 @@ public final class RcsContactUceCapability implements Parcelable { } } - private final Uri mContactUri; - private long mCapabilities; - private List<String> mExtensionTags = new ArrayList<>(); - private Map<Long, Uri> mServiceMap = new HashMap<>(); - /** - * Use {@link Builder} to build an instance of this interface. - * @param contact The URI associated with this capability information. - * @hide + * Builder to help construct {@link RcsContactUceCapability} instances when capabilities were + * queried through a presence server. */ - RcsContactUceCapability(@NonNull Uri contact) { - mContactUri = contact; - } + public static class PresenceBuilder { - private RcsContactUceCapability(Parcel in) { - mContactUri = in.readParcelable(Uri.class.getClassLoader()); - mCapabilities = in.readLong(); - in.readStringList(mExtensionTags); - // read mServiceMap as key,value pair - int mapSize = in.readInt(); - for (int i = 0; i < mapSize; i++) { - mServiceMap.put(in.readLong(), in.readParcelable(Uri.class.getClassLoader())); + private final RcsContactUceCapability mCapabilities; + + /** + * Create the builder, which can be used to set UCE capabilities as well as custom + * capability extensions. + * @param contact The contact URI that the capabilities are attached to. + * @param sourceType The type where the capabilities of this contact were retrieved from. + * @param requestResult the request result + */ + public PresenceBuilder(@NonNull Uri contact, @SourceType int sourceType, + @RequestResult int requestResult) { + mCapabilities = new RcsContactUceCapability(contact, CAPABILITY_MECHANISM_PRESENCE, + sourceType); + mCapabilities.mRequestResult = requestResult; } - } - public static final @NonNull Creator<RcsContactUceCapability> CREATOR = - new Creator<RcsContactUceCapability>() { - @Override - public RcsContactUceCapability createFromParcel(Parcel in) { - return new RcsContactUceCapability(in); + /** + * Add the {@link RcsContactPresenceTuple} into the capabilities instance. + * @param tuple The {@link RcsContactPresenceTuple} to be added into. + * @return this PresenceBuilder + */ + public @NonNull PresenceBuilder addCapabilityTuple(RcsContactPresenceTuple tuple) { + mCapabilities.mPresenceTuples.add(tuple); + return this; } - @Override - public RcsContactUceCapability[] newArray(int size) { - return new RcsContactUceCapability[size]; + /** + * Add the list of {@link RcsContactPresenceTuple} into the capabilities instance. + * @param tuples The list of the {@link RcsContactPresenceTuple} to be added into. + * @return this PresenceBuilder + */ + public @NonNull PresenceBuilder addCapabilityTuples(List<RcsContactPresenceTuple> tuples) { + mCapabilities.mPresenceTuples.addAll(tuples); + return this; } - }; + + /** + * @return the RcsContactUceCapability instance. + */ + public @NonNull RcsContactUceCapability build() { + return mCapabilities; + } + } + + private final Uri mContactUri; + private @SourceType int mSourceType; + private @CapabilityMechanism int mCapabilityMechanism; + private @RequestResult int mRequestResult; + + private final List<String> mFeatureTags = new ArrayList<>(); + private final List<RcsContactPresenceTuple> mPresenceTuples = new ArrayList<>(); + + private RcsContactUceCapability(@NonNull Uri contactUri, @CapabilityMechanism int mechanism, + @SourceType int sourceType) { + mContactUri = contactUri; + mCapabilityMechanism = mechanism; + mSourceType = sourceType; + } + + private RcsContactUceCapability(Parcel in) { + mContactUri = in.readParcelable(Uri.class.getClassLoader()); + mCapabilityMechanism = in.readInt(); + mSourceType = in.readInt(); + mRequestResult = in.readInt(); + in.readStringList(mFeatureTags); + in.readParcelableList(mPresenceTuples, RcsContactPresenceTuple.class.getClassLoader()); + } @Override public void writeToParcel(@NonNull Parcel out, int flags) { - out.writeParcelable(mContactUri, 0); - out.writeLong(mCapabilities); - out.writeStringList(mExtensionTags); - // write mServiceMap as key,value pairs - int mapSize = mServiceMap.keySet().size(); - out.writeInt(mapSize); - for (long key : mServiceMap.keySet()) { - out.writeLong(key); - out.writeParcelable(mServiceMap.get(key), 0); - } + out.writeParcelable(mContactUri, flags); + out.writeInt(mCapabilityMechanism); + out.writeInt(mSourceType); + out.writeInt(mRequestResult); + out.writeStringList(mFeatureTags); + out.writeParcelableList(mPresenceTuples, flags); } @Override @@ -266,49 +249,87 @@ public final class RcsContactUceCapability implements Parcelable { return 0; } + public static final @NonNull Creator<RcsContactUceCapability> CREATOR = + new Creator<RcsContactUceCapability>() { + @Override + public RcsContactUceCapability createFromParcel(Parcel in) { + return new RcsContactUceCapability(in); + } + + @Override + public RcsContactUceCapability[] newArray(int size) { + return new RcsContactUceCapability[size]; + } + }; + /** - * Query for a capability - * @param type The capability flag to query. - * @return true if the capability flag specified is set, false otherwise. + * @return The mechanism used to get the capabilities. */ - public boolean isCapable(@CapabilityFlag long type) { - return (mCapabilities & type) > 0; + public @CapabilityMechanism int getCapabilityMechanism() { + return mCapabilityMechanism; } /** - * @return true if the extension service tag is set, false otherwise. + * @return The feature tags present in the OPTIONS response from the network. + * <p> + * Note: this is only populated if {@link #getCapabilityMechanism} is + * {@link CAPABILITY_MECHANISM_OPTIONS} */ - public boolean isCapable(@NonNull String extensionTag) { - return mExtensionTags.contains(extensionTag); + public @NonNull List<String> getOptionsFeatureTags() { + if (mCapabilityMechanism != CAPABILITY_MECHANISM_OPTIONS) { + return Collections.emptyList(); + } + return Collections.unmodifiableList(mFeatureTags); } /** - * @return An immutable list containing all of the extension tags that have been set as capable. - * @throws UnsupportedOperationException if this list is modified. + * @return The tuple elements associated with the presence element portion of the PIDF document + * contained in the NOTIFY response from the network. + * <p> + * Note: this is only populated if {@link #getCapabilityMechanism} is + * {@link CAPABILITY_MECHANISM_PRESENCE} */ - public @NonNull List<String> getCapableExtensionTags() { - return Collections.unmodifiableList(mExtensionTags); + public @NonNull List<RcsContactPresenceTuple> getPresenceTuples() { + if (mCapabilityMechanism != CAPABILITY_MECHANISM_PRESENCE) { + return Collections.emptyList(); + } + return Collections.unmodifiableList(mPresenceTuples); } /** - * Retrieves the {@link Uri} associated with the capability being queried. - * <p> - * This will typically be the contact {@link Uri} available via {@link #getContactUri()} unless - * a different service {@link Uri} was associated with this capability using - * {@link Builder#add(long, Uri)}. + * Get the RcsContactPresenceTuple associated with the given service id. + * @param serviceId The service id to get the presence tuple. + * @return The RcsContactPresenceTuple which has the given service id. * - * @return a String containing the {@link Uri} associated with the service tag or - * {@code null} if this capability is not set as capable. - * @see #isCapable(long) + * <p> + * Note: this is only populated if {@link #getCapabilityMechanism} is + * {@link CAPABILITY_MECHANISM_PRESENCE} */ - public @Nullable Uri getServiceUri(@CapabilityFlag long type) { - Uri result = mServiceMap.getOrDefault(type, null); - // If the capability is capable, but does not have a service URI associated, use the default - // contact URI. - if (result == null) { - return isCapable(type) ? getContactUri() : null; + public @Nullable RcsContactPresenceTuple getPresenceTuple(String serviceId) { + if (mCapabilityMechanism != CAPABILITY_MECHANISM_PRESENCE) { + return null; } - return result; + for (RcsContactPresenceTuple tuple : mPresenceTuples) { + if (tuple.getServiceId().equals(serviceId)) { + return tuple; + } + } + return null; + } + + /** + * @return the source of the data that was used to populate the capabilities of the requested + * contact. + */ + public @SourceType int getSourceType() { + return mSourceType; + } + + /** + * @return the result of querying the capabilities of the requested contact. + */ + public @RequestResult int getRequestResult() { + return mRequestResult; } /** diff --git a/telephony/java/android/telephony/ims/RcsUceAdapter.java b/telephony/java/android/telephony/ims/RcsUceAdapter.java index 4606f7d625aa..c2ddcea3dbd3 100644 --- a/telephony/java/android/telephony/ims/RcsUceAdapter.java +++ b/telephony/java/android/telephony/ims/RcsUceAdapter.java @@ -22,7 +22,6 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.RequiresPermission; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.content.Context; import android.net.Uri; import android.os.Binder; @@ -519,7 +518,6 @@ public class RcsUceAdapter { * @hide */ @SystemApi - @TestApi @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) public void setUceSettingEnabled(boolean isEnabled) throws ImsException { IImsRcsController imsRcsController = getIImsRcsController(); diff --git a/telephony/java/android/telephony/ims/aidl/IImsServiceController.aidl b/telephony/java/android/telephony/ims/aidl/IImsServiceController.aidl index c956cbcc816c..c6966b3cf53e 100644 --- a/telephony/java/android/telephony/ims/aidl/IImsServiceController.aidl +++ b/telephony/java/android/telephony/ims/aidl/IImsServiceController.aidl @@ -21,6 +21,7 @@ import android.telephony.ims.aidl.IImsRcsFeature; import android.telephony.ims.aidl.IImsConfig; import android.telephony.ims.aidl.IImsRegistration; import android.telephony.ims.aidl.IImsServiceControllerListener; +import android.telephony.ims.aidl.ISipTransport; import android.telephony.ims.stub.ImsFeatureConfiguration; import com.android.ims.internal.IImsFeatureStatusCallback; @@ -34,6 +35,7 @@ interface IImsServiceController { IImsMmTelFeature createMmTelFeature(int slotId); IImsRcsFeature createRcsFeature(int slotId); ImsFeatureConfiguration querySupportedImsFeatures(); + long getImsServiceCapabilities(); void addFeatureStatusCallback(int slotId, int featureType, in IImsFeatureStatusCallback c); void removeFeatureStatusCallback(int slotId, int featureType, in IImsFeatureStatusCallback c); // Synchronous call to ensure the ImsService is ready before continuing with feature creation. @@ -41,6 +43,7 @@ interface IImsServiceController { void removeImsFeature(int slotId, int featureType); IImsConfig getConfig(int slotId); IImsRegistration getRegistration(int slotId); + ISipTransport getSipTransport(int slotId); oneway void enableIms(int slotId); oneway void disableIms(int slotId); } diff --git a/telephony/java/android/telephony/ims/aidl/ISipTransport.aidl b/telephony/java/android/telephony/ims/aidl/ISipTransport.aidl new file mode 100644 index 000000000000..fe233430ffad --- /dev/null +++ b/telephony/java/android/telephony/ims/aidl/ISipTransport.aidl @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telephony.ims.aidl; + +/** + * Interface for commands to the SIP Transport implementation. + * {@hide} + */ +interface ISipTransport { +} diff --git a/telephony/java/android/telephony/ims/feature/CapabilityChangeRequest.java b/telephony/java/android/telephony/ims/feature/CapabilityChangeRequest.java index 1918bcb00733..87a5094a95f3 100644 --- a/telephony/java/android/telephony/ims/feature/CapabilityChangeRequest.java +++ b/telephony/java/android/telephony/ims/feature/CapabilityChangeRequest.java @@ -18,7 +18,6 @@ package android.telephony.ims.feature; import android.annotation.NonNull; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.os.Parcel; import android.os.Parcelable; import android.telephony.ims.stub.ImsRegistrationImplBase; @@ -34,7 +33,6 @@ import java.util.Set; * {@hide} */ @SystemApi -@TestApi public final class CapabilityChangeRequest implements Parcelable { /** diff --git a/telephony/java/android/telephony/ims/feature/ImsFeature.java b/telephony/java/android/telephony/ims/feature/ImsFeature.java index e5779b315c93..b0a7b62c88ab 100644 --- a/telephony/java/android/telephony/ims/feature/ImsFeature.java +++ b/telephony/java/android/telephony/ims/feature/ImsFeature.java @@ -44,7 +44,6 @@ import java.util.Map; * @hide */ @SystemApi -@TestApi public abstract class ImsFeature { private static final String LOG_TAG = "ImsFeature"; @@ -62,19 +61,19 @@ public abstract class ImsFeature { * CSFB for emergency calling. * @hide */ - @SystemApi @TestApi + @SystemApi public static final int FEATURE_EMERGENCY_MMTEL = 0; /** * This feature supports the MMTEL feature. * @hide */ - @SystemApi @TestApi + @SystemApi public static final int FEATURE_MMTEL = 1; /** * This feature supports the RCS feature. * @hide */ - @SystemApi @TestApi + @SystemApi public static final int FEATURE_RCS = 2; /** * Total number of features defined @@ -124,7 +123,7 @@ public abstract class ImsFeature { * during this time will result in an {@link IllegalStateException}. * @hide */ - @SystemApi @TestApi + @SystemApi public static final int STATE_UNAVAILABLE = 0; /** * This {@link ImsFeature} state is initializing and should not be communicated with. This will @@ -132,14 +131,14 @@ public abstract class ImsFeature { * during this time will result in an {@link IllegalStateException}. * @hide */ - @SystemApi @TestApi + @SystemApi public static final int STATE_INITIALIZING = 1; /** * This {@link ImsFeature} is ready for communication. Do not attempt to call framework methods * until {@see #onFeatureReady()} is called. * @hide */ - @SystemApi @TestApi + @SystemApi public static final int STATE_READY = 2; /** @@ -169,13 +168,13 @@ public abstract class ImsFeature { * The capability was unable to be changed. * @hide */ - @SystemApi @TestApi + @SystemApi public static final int CAPABILITY_ERROR_GENERIC = -1; /** * The capability was able to be changed. * @hide */ - @SystemApi @TestApi + @SystemApi public static final int CAPABILITY_SUCCESS = 0; /** @@ -349,7 +348,7 @@ public abstract class ImsFeature { * subscription IDs associated with this slot. * @hide */ - @SystemApi @TestApi + @SystemApi public final int getSlotIndex() { return mSlotId; } @@ -359,7 +358,7 @@ public abstract class ImsFeature { * or {@link #STATE_UNAVAILABLE} if it has not been updated yet. * @hide */ - @SystemApi @TestApi + @SystemApi public @ImsState int getFeatureState() { synchronized (mLock) { return mState; @@ -373,7 +372,7 @@ public abstract class ImsFeature { * {@link #STATE_INITIALIZING}, or {@link #STATE_READY}. * @hide */ - @SystemApi @TestApi + @SystemApi public final void setFeatureState(@ImsState int state) { synchronized (mLock) { if (mState != state) { diff --git a/telephony/java/android/telephony/ims/feature/MmTelFeature.java b/telephony/java/android/telephony/ims/feature/MmTelFeature.java index d8a10ebb7bde..508d1a79d27e 100644 --- a/telephony/java/android/telephony/ims/feature/MmTelFeature.java +++ b/telephony/java/android/telephony/ims/feature/MmTelFeature.java @@ -20,7 +20,6 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.os.Bundle; import android.os.Message; import android.os.RemoteException; @@ -60,7 +59,7 @@ public class MmTelFeature extends ImsFeature { /** * @hide */ - @SystemApi @TestApi + @SystemApi public MmTelFeature() { } @@ -228,7 +227,7 @@ public class MmTelFeature extends ImsFeature { * @see #removeCapabilities(int) * @hide */ - @SystemApi @TestApi + @SystemApi public MmTelCapabilities() { super(); } @@ -237,7 +236,7 @@ public class MmTelFeature extends ImsFeature { * @hide */ @Deprecated - @SystemApi @TestApi + @SystemApi public MmTelCapabilities(Capabilities c) { mCapabilities = c.mCapabilities; } @@ -248,7 +247,7 @@ public class MmTelFeature extends ImsFeature { * bitfield. * @hide */ - @SystemApi @TestApi + @SystemApi public MmTelCapabilities(@MmTelCapability int capabilities) { super(capabilities); } @@ -288,7 +287,7 @@ public class MmTelFeature extends ImsFeature { * @hide */ @Override - @SystemApi @TestApi + @SystemApi public final void addCapabilities(@MmTelCapability int capabilities) { super.addCapabilities(capabilities); } @@ -297,7 +296,7 @@ public class MmTelFeature extends ImsFeature { * @hide */ @Override - @SystemApi @TestApi + @SystemApi public final void removeCapabilities(@MmTelCapability int capability) { super.removeCapabilities(capability); } @@ -375,14 +374,14 @@ public class MmTelFeature extends ImsFeature { * outgoing call as IMS. * @hide */ - @SystemApi @TestApi + @SystemApi public static final int PROCESS_CALL_IMS = 0; /** * To be returned by {@link #shouldProcessCall(String[])} when the telephony framework should * not process the outgoing call as IMS and should instead use circuit switch. * @hide */ - @SystemApi @TestApi + @SystemApi public static final int PROCESS_CALL_CSFB = 1; /** @hide */ @@ -400,7 +399,7 @@ public class MmTelFeature extends ImsFeature { * This is an optional boolean flag. * @hide */ - @SystemApi @TestApi + @SystemApi public static final String EXTRA_IS_USSD = "android.telephony.ims.feature.extra.IS_USSD"; /** @@ -413,7 +412,7 @@ public class MmTelFeature extends ImsFeature { * This is an optional boolean flag. * @hide */ - @SystemApi @TestApi + @SystemApi public static final String EXTRA_IS_UNKNOWN_CALL = "android.telephony.ims.feature.extra.IS_UNKNOWN_CALL"; @@ -453,7 +452,7 @@ public class MmTelFeature extends ImsFeature { * @hide */ @Override - @SystemApi @TestApi + @SystemApi public @NonNull final MmTelCapabilities queryCapabilityStatus() { return new MmTelCapabilities(super.queryCapabilityStatus()); } @@ -468,7 +467,7 @@ public class MmTelFeature extends ImsFeature { * {@link #changeEnabledCapabilities}) should also show the status as disabled. * @hide */ - @SystemApi @TestApi + @SystemApi public final void notifyCapabilitiesStatusChanged(@NonNull MmTelCapabilities c) { if (c == null) { throw new IllegalArgumentException("MmTelCapabilities must be non-null!"); @@ -483,7 +482,7 @@ public class MmTelFeature extends ImsFeature { * {@link #EXTRA_IS_UNKNOWN_CALL} and {@link #EXTRA_IS_USSD} above. * @hide */ - @SystemApi @TestApi + @SystemApi public final void notifyIncomingCall(@NonNull ImsCallSessionImplBase c, @NonNull Bundle extras) { if (c == null || extras == null) { @@ -509,7 +508,7 @@ public class MmTelFeature extends ImsFeature { * @param reason The {@link ImsReasonInfo} call rejection reason. * @hide */ - @SystemApi @TestApi + @SystemApi public final void notifyRejectedCall(@NonNull ImsCallProfile callProfile, @NonNull ImsReasonInfo reason) { if (callProfile == null || reason == null) { @@ -548,7 +547,7 @@ public class MmTelFeature extends ImsFeature { * @link count the new Voice Message count. * @hide */ - @SystemApi @TestApi + @SystemApi public final void notifyVoiceMessageCountUpdate(int count) { IImsMmTelListener listener = getListener(); if (listener == null) { @@ -571,7 +570,7 @@ public class MmTelFeature extends ImsFeature { * @hide */ @Override - @SystemApi @TestApi + @SystemApi public boolean queryCapabilityConfiguration(@MmTelCapabilities.MmTelCapability int capability, @ImsRegistrationImplBase.ImsRegistrationTech int radioTech) { // Base implementation - Override to provide functionality @@ -592,7 +591,7 @@ public class MmTelFeature extends ImsFeature { * * @hide */ @Override - @SystemApi @TestApi + @SystemApi public void changeEnabledCapabilities(@NonNull CapabilityChangeRequest request, @NonNull CapabilityCallbackProxy c) { // Base implementation, no-op @@ -617,7 +616,7 @@ public class MmTelFeature extends ImsFeature { * @return a {@link ImsCallProfile} object * @hide */ - @SystemApi @TestApi + @SystemApi public @Nullable ImsCallProfile createCallProfile(int callSessionType, int callType) { // Base Implementation - Should be overridden return null; @@ -640,7 +639,7 @@ public class MmTelFeature extends ImsFeature { * @param profile a call profile to make the call * @hide */ - @SystemApi @TestApi + @SystemApi public @Nullable ImsCallSessionImplBase createCallSession(@NonNull ImsCallProfile profile) { // Base Implementation - Should be overridden return null; @@ -659,7 +658,7 @@ public class MmTelFeature extends ImsFeature { * call will be placed over IMS or via CSFB. * @hide */ - @SystemApi @TestApi + @SystemApi public @ProcessCallResult int shouldProcessCall(@NonNull String[] numbers) { return PROCESS_CALL_IMS; } @@ -694,7 +693,7 @@ public class MmTelFeature extends ImsFeature { * configuration. * @hide */ - @SystemApi @TestApi + @SystemApi public @NonNull ImsUtImplBase getUt() { // Base Implementation - Should be overridden return new ImsUtImplBase(); @@ -705,7 +704,7 @@ public class MmTelFeature extends ImsFeature { * calls that support it. * @hide */ - @SystemApi @TestApi + @SystemApi public @NonNull ImsEcbmImplBase getEcbm() { // Base Implementation - Should be overridden return new ImsEcbmImplBase(); @@ -716,7 +715,7 @@ public class MmTelFeature extends ImsFeature { * package processing for multi-endpoint. * @hide */ - @SystemApi @TestApi + @SystemApi public @NonNull ImsMultiEndpointImplBase getMultiEndpoint() { // Base Implementation - Should be overridden return new ImsMultiEndpointImplBase(); @@ -744,7 +743,7 @@ public class MmTelFeature extends ImsFeature { * } * @hide */ - @SystemApi @TestApi + @SystemApi public void setUiTtyMode(int mode, @Nullable Message onCompleteMessage) { // Base Implementation - Should be overridden } @@ -780,7 +779,7 @@ public class MmTelFeature extends ImsFeature { * Provider. * @hide */ - @SystemApi @TestApi + @SystemApi public @NonNull ImsSmsImplBase getSmsImplementation() { return new ImsSmsImplBase(); } @@ -794,7 +793,7 @@ public class MmTelFeature extends ImsFeature { * @hide */ @Override - @SystemApi @TestApi + @SystemApi public void onFeatureRemoved() { // Base Implementation - Should be overridden } @@ -804,7 +803,7 @@ public class MmTelFeature extends ImsFeature { * @hide */ @Override - @SystemApi @TestApi + @SystemApi public void onFeatureReady() { // Base Implementation - Should be overridden } diff --git a/telephony/java/android/telephony/ims/feature/RcsFeature.java b/telephony/java/android/telephony/ims/feature/RcsFeature.java index 98b0bcf6075b..b8ae146784d4 100644 --- a/telephony/java/android/telephony/ims/feature/RcsFeature.java +++ b/telephony/java/android/telephony/ims/feature/RcsFeature.java @@ -20,7 +20,6 @@ import android.annotation.CallbackExecutor; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.net.Uri; import android.os.RemoteException; import android.telephony.ims.RcsContactUceCapability; @@ -50,7 +49,6 @@ import java.util.function.Supplier; * @hide */ @SystemApi -@TestApi public class RcsFeature extends ImsFeature { private static final String LOG_TAG = "RcsFeature"; diff --git a/telephony/java/android/telephony/ims/stub/ImsCallSessionImplBase.java b/telephony/java/android/telephony/ims/stub/ImsCallSessionImplBase.java index 8f738d216cbe..1cebdd582b58 100644 --- a/telephony/java/android/telephony/ims/stub/ImsCallSessionImplBase.java +++ b/telephony/java/android/telephony/ims/stub/ImsCallSessionImplBase.java @@ -18,7 +18,6 @@ package android.telephony.ims.stub; import android.annotation.NonNull; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.os.Message; import android.os.RemoteException; import android.telephony.ims.ImsCallProfile; @@ -39,7 +38,6 @@ import com.android.ims.internal.IImsVideoCallProvider; * @hide */ @SystemApi -@TestApi // DO NOT remove or change the existing APIs, only add new ones to this Base implementation or you // will break other implementations of ImsCallSession maintained by other ImsServices. public class ImsCallSessionImplBase implements AutoCloseable { diff --git a/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java b/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java index 4ef44d3ec9ef..e0290a5dc2af 100644 --- a/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java +++ b/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java @@ -19,7 +19,6 @@ package android.telephony.ims.stub; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.content.Context; import android.os.PersistableBundle; import android.os.RemoteException; @@ -51,7 +50,6 @@ import java.util.HashMap; * @hide */ @SystemApi -@TestApi public class ImsConfigImplBase { private static final String TAG = "ImsConfigImplBase"; diff --git a/telephony/java/android/telephony/ims/stub/ImsEcbmImplBase.java b/telephony/java/android/telephony/ims/stub/ImsEcbmImplBase.java index 4a3a2eaf4225..06c35eaec6dd 100644 --- a/telephony/java/android/telephony/ims/stub/ImsEcbmImplBase.java +++ b/telephony/java/android/telephony/ims/stub/ImsEcbmImplBase.java @@ -17,7 +17,6 @@ package android.telephony.ims.stub; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.os.RemoteException; import android.util.Log; @@ -34,7 +33,6 @@ import com.android.ims.internal.IImsEcbmListener; * @hide */ @SystemApi -@TestApi public class ImsEcbmImplBase { private static final String TAG = "ImsEcbmImplBase"; diff --git a/telephony/java/android/telephony/ims/stub/ImsFeatureConfiguration.java b/telephony/java/android/telephony/ims/stub/ImsFeatureConfiguration.java index 4e7307e2fa0c..cd9ebbf38e35 100644 --- a/telephony/java/android/telephony/ims/stub/ImsFeatureConfiguration.java +++ b/telephony/java/android/telephony/ims/stub/ImsFeatureConfiguration.java @@ -19,7 +19,6 @@ package android.telephony.ims.stub; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.os.Parcel; import android.os.Parcelable; import android.telephony.ims.feature.ImsFeature; @@ -36,7 +35,6 @@ import java.util.Set; * @hide */ @SystemApi -@TestApi public final class ImsFeatureConfiguration implements Parcelable { public static final class FeatureSlotPair { diff --git a/telephony/java/android/telephony/ims/stub/ImsMultiEndpointImplBase.java b/telephony/java/android/telephony/ims/stub/ImsMultiEndpointImplBase.java index 0ae5bba5d722..d002903a11b6 100644 --- a/telephony/java/android/telephony/ims/stub/ImsMultiEndpointImplBase.java +++ b/telephony/java/android/telephony/ims/stub/ImsMultiEndpointImplBase.java @@ -17,7 +17,6 @@ package android.telephony.ims.stub; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.os.RemoteException; import android.telephony.ims.ImsExternalCallState; import android.util.Log; @@ -38,7 +37,6 @@ import java.util.List; * @hide */ @SystemApi -@TestApi public class ImsMultiEndpointImplBase { private static final String TAG = "MultiEndpointImplBase"; diff --git a/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java b/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java index 2cdf70e6cf4c..12abdd1d7e11 100644 --- a/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java +++ b/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java @@ -18,7 +18,6 @@ package android.telephony.ims.stub; import android.annotation.IntDef; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.net.Uri; import android.os.RemoteException; import android.telephony.ims.ImsReasonInfo; @@ -40,7 +39,6 @@ import java.lang.annotation.RetentionPolicy; * @hide */ @SystemApi -@TestApi public class ImsRegistrationImplBase { private static final String LOG_TAG = "ImsRegistrationImplBase"; diff --git a/telephony/java/android/telephony/ims/stub/ImsSmsImplBase.java b/telephony/java/android/telephony/ims/stub/ImsSmsImplBase.java index a9a33c0e1507..2783e299236b 100644 --- a/telephony/java/android/telephony/ims/stub/ImsSmsImplBase.java +++ b/telephony/java/android/telephony/ims/stub/ImsSmsImplBase.java @@ -19,7 +19,6 @@ package android.telephony.ims.stub; import android.annotation.IntDef; import android.annotation.IntRange; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.os.RemoteException; import android.telephony.SmsManager; import android.telephony.SmsMessage; @@ -38,7 +37,6 @@ import java.lang.annotation.RetentionPolicy; * @hide */ @SystemApi -@TestApi public class ImsSmsImplBase { private static final String LOG_TAG = "SmsImplBase"; diff --git a/telephony/java/android/telephony/ims/stub/ImsUtImplBase.java b/telephony/java/android/telephony/ims/stub/ImsUtImplBase.java index 8564f7affd6d..f5219d5b49e8 100644 --- a/telephony/java/android/telephony/ims/stub/ImsUtImplBase.java +++ b/telephony/java/android/telephony/ims/stub/ImsUtImplBase.java @@ -20,7 +20,6 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.os.Bundle; import android.os.RemoteException; import android.telephony.ims.ImsUtListener; @@ -40,7 +39,6 @@ import java.lang.annotation.RetentionPolicy; // DO NOT remove or change the existing APIs, only add new ones to this Base implementation or you // will break other implementations of ImsUt maintained by other ImsServices. @SystemApi -@TestApi public class ImsUtImplBase { /** * Bar all incoming calls. (See 3GPP TS 24.611) diff --git a/telephony/java/android/telephony/ims/stub/SipTransportImplBase.java b/telephony/java/android/telephony/ims/stub/SipTransportImplBase.java new file mode 100644 index 000000000000..17bd4b14925f --- /dev/null +++ b/telephony/java/android/telephony/ims/stub/SipTransportImplBase.java @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telephony.ims.stub; + +import android.annotation.NonNull; +import android.telephony.ims.aidl.ISipTransport; + +import java.util.concurrent.Executor; + +/** + * Manages the creation and destruction of SipDelegates in order to proxy SIP traffic to other + * IMS applications in order to support IMS single registration. + * + * @hide Until there is an implementation, keep this hidden + */ +public class SipTransportImplBase { + + private final Executor mBinderExecutor; + private final ISipTransport mSipTransportImpl = new ISipTransport.Stub() { + + }; + + /** + * Create an implementation of SipTransportImplBase. + * + * @param executor The executor that remote calls from the framework should be called on. + */ + public SipTransportImplBase(@NonNull Executor executor) { + if (executor == null) { + throw new IllegalArgumentException("executor must not be null"); + } + + mBinderExecutor = executor; + } + + /** + * @return The IInterface used by the framework. + * @hide + */ + public ISipTransport getBinder() { + return mSipTransportImpl; + } +} diff --git a/telephony/java/android/telephony/mbms/DownloadRequest.java b/telephony/java/android/telephony/mbms/DownloadRequest.java index ac258cd40d65..eb59f87a6c02 100644 --- a/telephony/java/android/telephony/mbms/DownloadRequest.java +++ b/telephony/java/android/telephony/mbms/DownloadRequest.java @@ -19,7 +19,6 @@ package android.telephony.mbms; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.content.Intent; import android.net.Uri; import android.os.Parcel; @@ -186,7 +185,6 @@ public final class DownloadRequest implements Parcelable { * @hide */ @SystemApi - @TestApi public Builder setServiceId(String serviceId) { fileServiceId = serviceId; return this; diff --git a/telephony/java/android/telephony/mbms/FileInfo.java b/telephony/java/android/telephony/mbms/FileInfo.java index ada2872874f5..e52b2ce0c505 100644 --- a/telephony/java/android/telephony/mbms/FileInfo.java +++ b/telephony/java/android/telephony/mbms/FileInfo.java @@ -17,7 +17,6 @@ package android.telephony.mbms; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.net.Uri; import android.os.Parcel; import android.os.Parcelable; @@ -50,7 +49,6 @@ public final class FileInfo implements Parcelable { * @hide */ @SystemApi - @TestApi public FileInfo(Uri uri, String mimeType) { this.uri = uri; this.mimeType = mimeType; diff --git a/telephony/java/android/telephony/mbms/FileServiceInfo.java b/telephony/java/android/telephony/mbms/FileServiceInfo.java index 8c79ab63b131..8777e7f59e3f 100644 --- a/telephony/java/android/telephony/mbms/FileServiceInfo.java +++ b/telephony/java/android/telephony/mbms/FileServiceInfo.java @@ -17,7 +17,6 @@ package android.telephony.mbms; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.os.Parcel; import android.os.Parcelable; @@ -36,7 +35,6 @@ public final class FileServiceInfo extends ServiceInfo implements Parcelable { /** @hide */ @SystemApi - @TestApi public FileServiceInfo(Map<Locale, String> newNames, String newClassName, List<Locale> newLocales, String newServiceId, Date start, Date end, List<FileInfo> newFiles) { diff --git a/telephony/java/android/telephony/mbms/StreamingServiceInfo.java b/telephony/java/android/telephony/mbms/StreamingServiceInfo.java index 8ad1d8940ca7..316e86568213 100644 --- a/telephony/java/android/telephony/mbms/StreamingServiceInfo.java +++ b/telephony/java/android/telephony/mbms/StreamingServiceInfo.java @@ -17,7 +17,6 @@ package android.telephony.mbms; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.os.Parcel; import android.os.Parcelable; @@ -42,7 +41,6 @@ public final class StreamingServiceInfo extends ServiceInfo implements Parcelabl * @hide */ @SystemApi - @TestApi public StreamingServiceInfo(Map<Locale, String> names, String className, List<Locale> locales, String serviceId, Date start, Date end) { super(names, className, locales, serviceId, start, end); diff --git a/telephony/java/android/telephony/mbms/UriPathPair.java b/telephony/java/android/telephony/mbms/UriPathPair.java index f53d7e0287f3..9258919919b7 100644 --- a/telephony/java/android/telephony/mbms/UriPathPair.java +++ b/telephony/java/android/telephony/mbms/UriPathPair.java @@ -17,7 +17,6 @@ package android.telephony.mbms; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.content.ContentResolver; import android.net.Uri; import android.os.Parcel; @@ -30,7 +29,6 @@ import android.telephony.mbms.vendor.VendorUtils; * @hide */ @SystemApi -@TestApi public final class UriPathPair implements Parcelable { private final Uri mFilePathUri; private final Uri mContentUri; diff --git a/telephony/java/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java b/telephony/java/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java index 3053ea03bebe..ffc1d2efe2ae 100644 --- a/telephony/java/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java +++ b/telephony/java/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java @@ -18,7 +18,6 @@ package android.telephony.mbms.vendor; import android.annotation.NonNull; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.content.Intent; import android.os.Binder; import android.os.IBinder; @@ -45,7 +44,6 @@ import java.util.Map; * @hide */ @SystemApi -@TestApi public class MbmsDownloadServiceBase extends IMbmsDownloadService.Stub { private final Map<IBinder, DownloadStatusListener> mDownloadStatusListenerBinderMap = new HashMap<>(); @@ -576,7 +574,6 @@ public class MbmsDownloadServiceBase extends IMbmsDownloadService.Stub { // Following two methods exist to workaround b/124210145 /** @hide */ @SystemApi - @TestApi @Override public android.os.IBinder asBinder() { return super.asBinder(); @@ -584,7 +581,6 @@ public class MbmsDownloadServiceBase extends IMbmsDownloadService.Stub { /** @hide */ @SystemApi - @TestApi @Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws RemoteException { diff --git a/telephony/java/android/telephony/mbms/vendor/MbmsGroupCallServiceBase.java b/telephony/java/android/telephony/mbms/vendor/MbmsGroupCallServiceBase.java index 1335b52673d2..e5b18bb5d644 100644 --- a/telephony/java/android/telephony/mbms/vendor/MbmsGroupCallServiceBase.java +++ b/telephony/java/android/telephony/mbms/vendor/MbmsGroupCallServiceBase.java @@ -18,7 +18,6 @@ package android.telephony.mbms.vendor; import android.annotation.NonNull; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.app.Service; import android.content.Intent; import android.os.Binder; @@ -41,7 +40,6 @@ import java.util.List; * @hide */ @SystemApi -@TestApi public class MbmsGroupCallServiceBase extends Service { private final IBinder mInterface = new Stub() { @Override diff --git a/telephony/java/android/telephony/mbms/vendor/MbmsStreamingServiceBase.java b/telephony/java/android/telephony/mbms/vendor/MbmsStreamingServiceBase.java index cced44759527..e169b16ca958 100644 --- a/telephony/java/android/telephony/mbms/vendor/MbmsStreamingServiceBase.java +++ b/telephony/java/android/telephony/mbms/vendor/MbmsStreamingServiceBase.java @@ -18,7 +18,6 @@ package android.telephony.mbms.vendor; import android.annotation.Nullable; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.content.Intent; import android.net.Uri; import android.os.Binder; @@ -39,7 +38,6 @@ import java.util.List; * @hide */ @SystemApi -@TestApi public class MbmsStreamingServiceBase extends IMbmsStreamingService.Stub { /** * Initialize streaming service for this app and subId, registering the listener. @@ -299,7 +297,6 @@ public class MbmsStreamingServiceBase extends IMbmsStreamingService.Stub { // Following two methods exist to workaround b/124210145 /** @hide */ @SystemApi - @TestApi @Override public android.os.IBinder asBinder() { return super.asBinder(); @@ -307,7 +304,6 @@ public class MbmsStreamingServiceBase extends IMbmsStreamingService.Stub { /** @hide */ @SystemApi - @TestApi @Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws RemoteException { diff --git a/telephony/java/android/telephony/mbms/vendor/VendorUtils.java b/telephony/java/android/telephony/mbms/vendor/VendorUtils.java index f1cac8cf8286..a43f12244168 100644 --- a/telephony/java/android/telephony/mbms/vendor/VendorUtils.java +++ b/telephony/java/android/telephony/mbms/vendor/VendorUtils.java @@ -17,7 +17,6 @@ package android.telephony.mbms.vendor; import android.annotation.SystemApi; -import android.annotation.TestApi; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -35,7 +34,6 @@ import java.util.List; * @hide */ @SystemApi -@TestApi public class VendorUtils { /** diff --git a/telephony/java/com/android/ims/ImsFeatureContainer.java b/telephony/java/com/android/ims/ImsFeatureContainer.java index b259679ea1bf..80c1d43480cc 100644 --- a/telephony/java/com/android/ims/ImsFeatureContainer.java +++ b/telephony/java/com/android/ims/ImsFeatureContainer.java @@ -17,12 +17,14 @@ package com.android.ims; import android.annotation.NonNull; +import android.annotation.Nullable; import android.os.IBinder; import android.os.Parcel; import android.os.Parcelable; import android.telephony.ims.ImsService; import android.telephony.ims.aidl.IImsConfig; import android.telephony.ims.aidl.IImsRegistration; +import android.telephony.ims.aidl.ISipTransport; import android.telephony.ims.feature.ImsFeature; import java.util.Objects; @@ -49,6 +51,11 @@ public final class ImsFeatureContainer implements Parcelable { public final IImsRegistration imsRegistration; /** + * An optional interface containing the SIP transport implementation from the ImsService. + */ + public final ISipTransport sipTransport; + + /** * State of the feature that is being tracked. */ private @ImsFeature.ImsState int mState = ImsFeature.STATE_UNAVAILABLE; @@ -66,10 +73,11 @@ public final class ImsFeatureContainer implements Parcelable { * @param initialCaps The initial capabilities that the ImsService supports. */ public ImsFeatureContainer(@NonNull IBinder iFace, @NonNull IImsConfig iConfig, - @NonNull IImsRegistration iReg, long initialCaps) { + @NonNull IImsRegistration iReg, @Nullable ISipTransport transport, long initialCaps) { imsFeature = iFace; imsConfig = iConfig; imsRegistration = iReg; + sipTransport = transport; mCapabilities = initialCaps; } @@ -80,6 +88,7 @@ public final class ImsFeatureContainer implements Parcelable { imsFeature = in.readStrongBinder(); imsConfig = IImsConfig.Stub.asInterface(in.readStrongBinder()); imsRegistration = IImsRegistration.Stub.asInterface(in.readStrongBinder()); + sipTransport = ISipTransport.Stub.asInterface(in.readStrongBinder()); mState = in.readInt(); mCapabilities = in.readLong(); } @@ -123,13 +132,15 @@ public final class ImsFeatureContainer implements Parcelable { return imsFeature.equals(that.imsFeature) && imsConfig.equals(that.imsConfig) && imsRegistration.equals(that.imsRegistration) && + sipTransport.equals(that.sipTransport) && mState == that.getState() && mCapabilities == that.getCapabilities(); } @Override public int hashCode() { - return Objects.hash(imsFeature, imsConfig, imsRegistration, mState, mCapabilities); + return Objects.hash(imsFeature, imsConfig, imsRegistration, sipTransport, mState, + mCapabilities); } @Override @@ -138,6 +149,7 @@ public final class ImsFeatureContainer implements Parcelable { "imsFeature=" + imsFeature + ", imsConfig=" + imsConfig + ", imsRegistration=" + imsRegistration + + ", sipTransport=" + sipTransport + ", state=" + ImsFeature.STATE_LOG_MAP.get(mState) + ", capabilities = " + ImsService.getCapabilitiesString(mCapabilities) + '}'; @@ -153,6 +165,7 @@ public final class ImsFeatureContainer implements Parcelable { dest.writeStrongBinder(imsFeature); dest.writeStrongInterface(imsConfig); dest.writeStrongInterface(imsRegistration); + dest.writeStrongInterface(sipTransport); dest.writeInt(mState); dest.writeLong(mCapabilities); } diff --git a/test-mock/api/test-current.txt b/test-mock/api/test-current.txt index 32ca250b6c74..79d746a57cc5 100644 --- a/test-mock/api/test-current.txt +++ b/test-mock/api/test-current.txt @@ -6,21 +6,12 @@ package android.test.mock { } @Deprecated public class MockPackageManager extends android.content.pm.PackageManager { - method public void addOnPermissionsChangeListener(android.content.pm.PackageManager.OnPermissionsChangedListener); - method public boolean arePermissionsIndividuallyControlled(); - method public String getDefaultBrowserPackageNameAsUser(int); method public int getInstallReason(String, android.os.UserHandle); method public java.util.List<android.content.pm.ApplicationInfo> getInstalledApplicationsAsUser(int, int); - method public java.util.List<android.content.pm.PackageInfo> getInstalledPackagesAsUser(int, int); method public String[] getNamesForUids(int[]); method public String getPermissionControllerPackageName(); - method public int getPermissionFlags(String, String, android.os.UserHandle); method @NonNull public String getServicesSystemSharedLibraryPackageName(); method @NonNull public String getSharedSystemSharedLibraryPackageName(); - method public void grantRuntimePermission(String, String, android.os.UserHandle); - method public void removeOnPermissionsChangeListener(android.content.pm.PackageManager.OnPermissionsChangedListener); - method public void revokeRuntimePermission(String, String, android.os.UserHandle); - method public void updatePermissionFlags(String, String, int, int, android.os.UserHandle); } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt index 8ad6c46b913d..b29fae30565e 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt @@ -147,8 +147,7 @@ class SeamlessAppRotationTest( navBarLayerIsAlwaysVisible(bugId = 140855415) statusBarLayerIsAlwaysVisible(bugId = 140855415) noUncoveredRegions(configuration.startRotation, - configuration.endRotation, allStates = false - /*, bugId = 147659548*/) + configuration.endRotation, allStates = false, bugId = 147659548) navBarLayerRotatesAndScales(configuration.startRotation, configuration.endRotation) statusBarLayerRotatesScales(configuration.startRotation, @@ -161,7 +160,7 @@ class SeamlessAppRotationTest( val endingBounds = WindowUtils .getDisplayBounds(configuration.endRotation) - all("appLayerRotates"/*, bugId = 147659548*/) { + all("appLayerRotates", bugId = 147659548) { if (startingBounds == endingBounds) { this.hasVisibleRegion( configuration.intentPackageName, startingBounds) diff --git a/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java b/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java index 9bf6b6f2aaa4..725bfa95166a 100644 --- a/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java +++ b/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java @@ -27,6 +27,7 @@ import static org.junit.Assert.fail; import static org.junit.Assume.assumeTrue; import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper; +import com.android.ddmlib.Log; import com.android.tradefed.device.DeviceNotAvailableException; import com.android.tradefed.device.IFileEntry; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; @@ -53,6 +54,7 @@ import java.util.stream.Collectors; */ @RunWith(DeviceJUnit4ClassRunner.class) public class StagedRollbackTest extends BaseHostJUnit4Test { + private static final String TAG = "StagedRollbackTest"; private static final int NATIVE_CRASHES_THRESHOLD = 5; /** @@ -126,12 +128,14 @@ public class StagedRollbackTest extends BaseHostJUnit4Test { } if (found) { - if (!getDevice().isAdbRoot()) { + try { getDevice().enableAdbRoot(); - } - getDevice().remountSystemWritable(); - for (String file : files) { - getDevice().executeShellCommand("rm -rf " + file); + getDevice().remountSystemWritable(); + for (String file : files) { + getDevice().executeShellCommand("rm -rf " + file); + } + } finally { + getDevice().disableAdbRoot(); } getDevice().reboot(); } @@ -276,7 +280,7 @@ public class StagedRollbackTest extends BaseHostJUnit4Test { after.removeAll(before); // There should be only one /data/misc_ce/0/rollback/<rollbackId> created during test assertThat(after).hasSize(1); - after.forEach(dir -> assertDirectoryIsEmpty(dir)); + assertDirectoryIsEmpty(after.get(0)); } /** @@ -332,31 +336,37 @@ public class StagedRollbackTest extends BaseHostJUnit4Test { String oldFilePath1 = apexDataDirDeSys(APK_IN_APEX_TESTAPEX_NAME) + "/" + TEST_FILENAME_1; String oldFilePath2 = apexDataDirDeSys(APK_IN_APEX_TESTAPEX_NAME) + TEST_SUBDIR + TEST_FILENAME_2; - assertTrue(getDevice().pushString(TEST_STRING_1, oldFilePath1)); - assertTrue(getDevice().pushString(TEST_STRING_2, oldFilePath2)); + runAsRoot(() -> { + assertThat(getDevice().pushString(TEST_STRING_1, oldFilePath1)).isTrue(); + assertThat(getDevice().pushString(TEST_STRING_2, oldFilePath2)).isTrue(); + }); // Install new version of the APEX with rollback enabled runPhase("testRollbackApexDataDirectories_Phase1"); getDevice().reboot(); // Replace files in data directory - getDevice().deleteFile(oldFilePath1); - getDevice().deleteFile(oldFilePath2); String newFilePath3 = apexDataDirDeSys(APK_IN_APEX_TESTAPEX_NAME) + "/" + TEST_FILENAME_3; String newFilePath4 = apexDataDirDeSys(APK_IN_APEX_TESTAPEX_NAME) + TEST_SUBDIR + TEST_FILENAME_4; - assertTrue(getDevice().pushString(TEST_STRING_3, newFilePath3)); - assertTrue(getDevice().pushString(TEST_STRING_4, newFilePath4)); + runAsRoot(() -> { + getDevice().deleteFile(oldFilePath1); + getDevice().deleteFile(oldFilePath2); + assertThat(getDevice().pushString(TEST_STRING_3, newFilePath3)).isTrue(); + assertThat(getDevice().pushString(TEST_STRING_4, newFilePath4)).isTrue(); + }); // Roll back the APEX runPhase("testRollbackApexDataDirectories_Phase2"); getDevice().reboot(); // Verify that old files have been restored and new files are gone - assertEquals(TEST_STRING_1, getDevice().pullFileContents(oldFilePath1)); - assertEquals(TEST_STRING_2, getDevice().pullFileContents(oldFilePath2)); - assertNull(getDevice().pullFile(newFilePath3)); - assertNull(getDevice().pullFile(newFilePath4)); + runAsRoot(() -> { + assertEquals(TEST_STRING_1, getDevice().pullFileContents(oldFilePath1)); + assertEquals(TEST_STRING_2, getDevice().pullFileContents(oldFilePath2)); + assertNull(getDevice().pullFile(newFilePath3)); + assertNull(getDevice().pullFile(newFilePath4)); + }); // Verify snapshots are deleted after restoration List<String> after = getSnapshotDirectories("/data/misc/apexrollback"); @@ -364,7 +374,7 @@ public class StagedRollbackTest extends BaseHostJUnit4Test { after.removeAll(before); // There should be only one /data/misc/apexrollback/<rollbackId> created during test assertThat(after).hasSize(1); - after.forEach(dir -> assertDirectoryIsEmpty(dir)); + assertDirectoryIsEmpty(after.get(0)); } /** @@ -380,32 +390,38 @@ public class StagedRollbackTest extends BaseHostJUnit4Test { APK_IN_APEX_TESTAPEX_NAME, 0) + "/" + TEST_FILENAME_1; String oldFilePath2 = apexDataDirDeUser(APK_IN_APEX_TESTAPEX_NAME, 0) + TEST_SUBDIR + TEST_FILENAME_2; - assertTrue(getDevice().pushString(TEST_STRING_1, oldFilePath1)); - assertTrue(getDevice().pushString(TEST_STRING_2, oldFilePath2)); + runAsRoot(() -> { + assertThat(getDevice().pushString(TEST_STRING_1, oldFilePath1)).isTrue(); + assertThat(getDevice().pushString(TEST_STRING_2, oldFilePath2)).isTrue(); + }); // Install new version of the APEX with rollback enabled runPhase("testRollbackApexDataDirectories_Phase1"); getDevice().reboot(); // Replace files in data directory - getDevice().deleteFile(oldFilePath1); - getDevice().deleteFile(oldFilePath2); String newFilePath3 = apexDataDirDeUser(APK_IN_APEX_TESTAPEX_NAME, 0) + "/" + TEST_FILENAME_3; String newFilePath4 = apexDataDirDeUser(APK_IN_APEX_TESTAPEX_NAME, 0) + TEST_SUBDIR + TEST_FILENAME_4; - assertTrue(getDevice().pushString(TEST_STRING_3, newFilePath3)); - assertTrue(getDevice().pushString(TEST_STRING_4, newFilePath4)); + runAsRoot(() -> { + getDevice().deleteFile(oldFilePath1); + getDevice().deleteFile(oldFilePath2); + assertThat(getDevice().pushString(TEST_STRING_3, newFilePath3)).isTrue(); + assertThat(getDevice().pushString(TEST_STRING_4, newFilePath4)).isTrue(); + }); // Roll back the APEX runPhase("testRollbackApexDataDirectories_Phase2"); getDevice().reboot(); // Verify that old files have been restored and new files are gone - assertEquals(TEST_STRING_1, getDevice().pullFileContents(oldFilePath1)); - assertEquals(TEST_STRING_2, getDevice().pullFileContents(oldFilePath2)); - assertNull(getDevice().pullFile(newFilePath3)); - assertNull(getDevice().pullFile(newFilePath4)); + runAsRoot(() -> { + assertEquals(TEST_STRING_1, getDevice().pullFileContents(oldFilePath1)); + assertEquals(TEST_STRING_2, getDevice().pullFileContents(oldFilePath2)); + assertNull(getDevice().pullFile(newFilePath3)); + assertNull(getDevice().pullFile(newFilePath4)); + }); // Verify snapshots are deleted after restoration List<String> after = getSnapshotDirectories("/data/misc_de/0/apexrollback"); @@ -413,7 +429,7 @@ public class StagedRollbackTest extends BaseHostJUnit4Test { after.removeAll(before); // There should be only one /data/misc_de/0/apexrollback/<rollbackId> created during test assertThat(after).hasSize(1); - after.forEach(dir -> assertDirectoryIsEmpty(dir)); + assertDirectoryIsEmpty(after.get(0)); } /** @@ -428,31 +444,37 @@ public class StagedRollbackTest extends BaseHostJUnit4Test { String oldFilePath1 = apexDataDirCe(APK_IN_APEX_TESTAPEX_NAME, 0) + "/" + TEST_FILENAME_1; String oldFilePath2 = apexDataDirCe(APK_IN_APEX_TESTAPEX_NAME, 0) + TEST_SUBDIR + TEST_FILENAME_2; - assertTrue(getDevice().pushString(TEST_STRING_1, oldFilePath1)); - assertTrue(getDevice().pushString(TEST_STRING_2, oldFilePath2)); + runAsRoot(() -> { + assertThat(getDevice().pushString(TEST_STRING_1, oldFilePath1)).isTrue(); + assertThat(getDevice().pushString(TEST_STRING_2, oldFilePath2)).isTrue(); + }); // Install new version of the APEX with rollback enabled runPhase("testRollbackApexDataDirectories_Phase1"); getDevice().reboot(); // Replace files in data directory - getDevice().deleteFile(oldFilePath1); - getDevice().deleteFile(oldFilePath2); String newFilePath3 = apexDataDirCe(APK_IN_APEX_TESTAPEX_NAME, 0) + "/" + TEST_FILENAME_3; String newFilePath4 = apexDataDirCe(APK_IN_APEX_TESTAPEX_NAME, 0) + TEST_SUBDIR + TEST_FILENAME_4; - assertTrue(getDevice().pushString(TEST_STRING_3, newFilePath3)); - assertTrue(getDevice().pushString(TEST_STRING_4, newFilePath4)); + runAsRoot(() -> { + getDevice().deleteFile(oldFilePath1); + getDevice().deleteFile(oldFilePath2); + assertThat(getDevice().pushString(TEST_STRING_3, newFilePath3)).isTrue(); + assertThat(getDevice().pushString(TEST_STRING_4, newFilePath4)).isTrue(); + }); // Roll back the APEX runPhase("testRollbackApexDataDirectories_Phase2"); getDevice().reboot(); // Verify that old files have been restored and new files are gone - assertEquals(TEST_STRING_1, getDevice().pullFileContents(oldFilePath1)); - assertEquals(TEST_STRING_2, getDevice().pullFileContents(oldFilePath2)); - assertNull(getDevice().pullFile(newFilePath3)); - assertNull(getDevice().pullFile(newFilePath4)); + runAsRoot(() -> { + assertEquals(TEST_STRING_1, getDevice().pullFileContents(oldFilePath1)); + assertEquals(TEST_STRING_2, getDevice().pullFileContents(oldFilePath2)); + assertNull(getDevice().pullFile(newFilePath3)); + assertNull(getDevice().pullFile(newFilePath4)); + }); // Verify snapshots are deleted after restoration List<String> after = getSnapshotDirectories("/data/misc_ce/0/apexrollback"); @@ -460,7 +482,7 @@ public class StagedRollbackTest extends BaseHostJUnit4Test { after.removeAll(before); // There should be only one /data/misc_ce/0/apexrollback/<rollbackId> created during test assertThat(after).hasSize(1); - after.forEach(dir -> assertDirectoryIsEmpty(dir)); + assertDirectoryIsEmpty(after.get(0)); } /** @@ -474,30 +496,36 @@ public class StagedRollbackTest extends BaseHostJUnit4Test { // Push files to apk data directory String oldFilePath1 = apkDataDirDe(TESTAPP_A, 0) + "/" + TEST_FILENAME_1; String oldFilePath2 = apkDataDirDe(TESTAPP_A, 0) + TEST_SUBDIR + TEST_FILENAME_2; - assertTrue(getDevice().pushString(TEST_STRING_1, oldFilePath1)); - assertTrue(getDevice().pushString(TEST_STRING_2, oldFilePath2)); + runAsRoot(() -> { + assertThat(getDevice().pushString(TEST_STRING_1, oldFilePath1)).isTrue(); + assertThat(getDevice().pushString(TEST_STRING_2, oldFilePath2)).isTrue(); + }); // Install version 2 of TESTAPP_A with rollback enabled runPhase("testRollbackApkDataDirectories_Phase2"); getDevice().reboot(); // Replace files in data directory - getDevice().deleteFile(oldFilePath1); - getDevice().deleteFile(oldFilePath2); String newFilePath3 = apkDataDirDe(TESTAPP_A, 0) + "/" + TEST_FILENAME_3; String newFilePath4 = apkDataDirDe(TESTAPP_A, 0) + TEST_SUBDIR + TEST_FILENAME_4; - assertTrue(getDevice().pushString(TEST_STRING_3, newFilePath3)); - assertTrue(getDevice().pushString(TEST_STRING_4, newFilePath4)); + runAsRoot(() -> { + getDevice().deleteFile(oldFilePath1); + getDevice().deleteFile(oldFilePath2); + assertThat(getDevice().pushString(TEST_STRING_3, newFilePath3)).isTrue(); + assertThat(getDevice().pushString(TEST_STRING_4, newFilePath4)).isTrue(); + }); // Roll back the APK runPhase("testRollbackApkDataDirectories_Phase3"); getDevice().reboot(); // Verify that old files have been restored and new files are gone - assertEquals(TEST_STRING_1, getDevice().pullFileContents(oldFilePath1)); - assertEquals(TEST_STRING_2, getDevice().pullFileContents(oldFilePath2)); - assertNull(getDevice().pullFile(newFilePath3)); - assertNull(getDevice().pullFile(newFilePath4)); + runAsRoot(() -> { + assertEquals(TEST_STRING_1, getDevice().pullFileContents(oldFilePath1)); + assertEquals(TEST_STRING_2, getDevice().pullFileContents(oldFilePath2)); + assertNull(getDevice().pullFile(newFilePath3)); + assertNull(getDevice().pullFile(newFilePath4)); + }); } @Test @@ -509,8 +537,10 @@ public class StagedRollbackTest extends BaseHostJUnit4Test { String oldFilePath1 = apexDataDirCe(APK_IN_APEX_TESTAPEX_NAME, 0) + "/" + TEST_FILENAME_1; String oldFilePath2 = apexDataDirCe(APK_IN_APEX_TESTAPEX_NAME, 0) + TEST_SUBDIR + TEST_FILENAME_2; - assertTrue(getDevice().pushString(TEST_STRING_1, oldFilePath1)); - assertTrue(getDevice().pushString(TEST_STRING_2, oldFilePath2)); + runAsRoot(() -> { + assertThat(getDevice().pushString(TEST_STRING_1, oldFilePath1)).isTrue(); + assertThat(getDevice().pushString(TEST_STRING_2, oldFilePath2)).isTrue(); + }); // Install new version of the APEX with rollback enabled runPhase("testRollbackApexDataDirectories_Phase1"); @@ -523,20 +553,24 @@ public class StagedRollbackTest extends BaseHostJUnit4Test { assertThat(after).hasSize(1); // Expire all rollbacks and check CE snapshot directories are deleted runPhase("testCleanUp"); - for (String dir : after) { - assertNull(getDevice().getFileEntry(dir)); - } + runAsRoot(() -> { + for (String dir : after) { + assertNull(getDevice().getFileEntry(dir)); + } + }); } private void pushTestApex() throws Exception { CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(getBuild()); final String fileName = APK_IN_APEX_TESTAPEX_NAME + "_v1.apex"; final File apex = buildHelper.getTestFile(fileName); - if (!getDevice().isAdbRoot()) { + try { getDevice().enableAdbRoot(); + getDevice().remountSystemWritable(); + assertTrue(getDevice().pushFile(apex, "/system/apex/" + fileName)); + } finally { + getDevice().disableAdbRoot(); } - getDevice().remountSystemWritable(); - assertTrue(getDevice().pushFile(apex, "/system/apex/" + fileName)); getDevice().reboot(); } @@ -556,25 +590,35 @@ public class StagedRollbackTest extends BaseHostJUnit4Test { return String.format("/data/user_de/%d/%s", userId, apexName); } - private List<String> getSnapshotDirectories(String baseDir) { + private List<String> getSnapshotDirectories(String baseDir) throws Exception { try { - return getDevice().getFileEntry(baseDir).getChildren(false) + getDevice().enableAdbRoot(); + IFileEntry f = getDevice().getFileEntry(baseDir); + if (f == null) { + Log.d(TAG, "baseDir doesn't exist: " + baseDir); + return Collections.EMPTY_LIST; + } + List<String> list = f.getChildren(false) .stream().filter(entry -> entry.getName().matches("\\d+(-prerestore)?")) .map(entry -> entry.getFullPath()) .collect(Collectors.toList()); - } catch (Exception e) { - // Return an empty list if any error - return Collections.EMPTY_LIST; + Log.d(TAG, "getSnapshotDirectories=" + list); + return list; + } finally { + getDevice().disableAdbRoot(); } } - private void assertDirectoryIsEmpty(String path) { + private void assertDirectoryIsEmpty(String path) throws Exception { try { + getDevice().enableAdbRoot(); IFileEntry file = getDevice().getFileEntry(path); assertTrue("Not a directory: " + path, file.isDirectory()); assertTrue("Directory not empty: " + path, file.getChildren(false).isEmpty()); } catch (DeviceNotAvailableException e) { fail("Can't access directory: " + path); + } finally { + getDevice().disableAdbRoot(); } } @@ -612,4 +656,18 @@ public class StagedRollbackTest extends BaseHostJUnit4Test { return false; } } + + @FunctionalInterface + private interface ExceptionalRunnable { + void run() throws Exception; + } + + private void runAsRoot(ExceptionalRunnable runnable) throws Exception { + try { + getDevice().enableAdbRoot(); + runnable.run(); + } finally { + getDevice().disableAdbRoot(); + } + } } diff --git a/tests/net/Android.bp b/tests/net/Android.bp index 0fe84abcbc7b..a7622198cec7 100644 --- a/tests/net/Android.bp +++ b/tests/net/Android.bp @@ -50,6 +50,7 @@ android_test { platform_apis: true, test_suites: ["device-tests"], certificate: "platform", + jarjar_rules: "jarjar-rules.txt", static_libs: [ "androidx.test.rules", "FrameworksNetCommonTests", @@ -59,6 +60,7 @@ android_test { "mockito-target-minus-junit4", "net-tests-utils", "platform-test-annotations", + "service-connectivity", "services.core", "services.net", ], diff --git a/tests/net/TEST_MAPPING b/tests/net/TEST_MAPPING index 005cbe9ffdc4..89fc6ea2c47b 100644 --- a/tests/net/TEST_MAPPING +++ b/tests/net/TEST_MAPPING @@ -8,5 +8,10 @@ { "name": "FrameworksNetDeflakeTest" } + ], + "imports": [ + { + "path": "cts/tests/tests/net" + } ] }
\ No newline at end of file diff --git a/tests/net/common/Android.bp b/tests/net/common/Android.bp index 46d680fc4511..373aac604b2a 100644 --- a/tests/net/common/Android.bp +++ b/tests/net/common/Android.bp @@ -25,6 +25,7 @@ java_library { "junit", "mockito-target-minus-junit4", "net-tests-utils", + "net-utils-framework-common", "platform-test-annotations", ], libs: [ diff --git a/tests/net/common/java/android/net/LinkPropertiesTest.java b/tests/net/common/java/android/net/LinkPropertiesTest.java index 3c3076f11727..550953d0612d 100644 --- a/tests/net/common/java/android/net/LinkPropertiesTest.java +++ b/tests/net/common/java/android/net/LinkPropertiesTest.java @@ -32,7 +32,6 @@ import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import android.net.LinkProperties.ProvisioningChange; -import android.net.util.LinkPropertiesUtils.CompareResult; import android.os.Build; import android.system.OsConstants; import android.util.ArraySet; @@ -41,6 +40,7 @@ import androidx.core.os.BuildCompat; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; +import com.android.net.module.util.LinkPropertiesUtils.CompareResult; import com.android.testutils.DevSdkIgnoreRule; import com.android.testutils.DevSdkIgnoreRule.IgnoreAfter; import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo; @@ -52,7 +52,6 @@ import org.junit.runner.RunWith; import java.net.Inet4Address; import java.net.Inet6Address; import java.net.InetAddress; -import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; @@ -447,23 +446,21 @@ public class LinkPropertiesTest { assertEquals(3, lp.getRoutes().size()); assertAllRoutesHaveInterface("wlan0", lp); - // Check comparisons work. + // Check routes are updated correctly when calling setInterfaceName. LinkProperties lp2 = new LinkProperties(lp); assertAllRoutesHaveInterface("wlan0", lp2); - // LinkProperties#compareAllRoutes exists both in R and before R, but the return type - // changed in R, so a test compiled with the R version of LinkProperties cannot run on Q. - if (isAtLeastR()) { - assertEquals(0, lp.compareAllRoutes(lp2).added.size()); - assertEquals(0, lp.compareAllRoutes(lp2).removed.size()); - } + final CompareResult<RouteInfo> cr1 = + new CompareResult<>(lp.getAllRoutes(), lp2.getAllRoutes()); + assertEquals(0, cr1.added.size()); + assertEquals(0, cr1.removed.size()); lp2.setInterfaceName("p2p0"); assertAllRoutesHaveInterface("p2p0", lp2); assertAllRoutesNotHaveInterface("wlan0", lp2); - if (isAtLeastR()) { - assertEquals(3, lp.compareAllRoutes(lp2).added.size()); - assertEquals(3, lp.compareAllRoutes(lp2).removed.size()); - } + final CompareResult<RouteInfo> cr2 = + new CompareResult<>(lp.getAllRoutes(), lp2.getAllRoutes()); + assertEquals(3, cr2.added.size()); + assertEquals(3, cr2.removed.size()); // Remove route with incorrect interface, no route removed. lp.removeRoute(new RouteInfo(prefix2, null, null)); @@ -954,28 +951,6 @@ public class LinkPropertiesTest { assertTrue(rmnet3.getAllRoutes().isEmpty()); rmnet3.ensureDirectlyConnectedRoutes(); assertEqualRoutes(Collections.singletonList(directRoute3), rmnet3.getAllRoutes()); - - } - - @Test @IgnoreUpTo(Build.VERSION_CODES.Q) - public void testCompareResult() { - // Either adding or removing items - compareResult(Arrays.asList(1, 2, 3, 4), Arrays.asList(1), - Arrays.asList(2, 3, 4), new ArrayList<>()); - compareResult(Arrays.asList(1, 2), Arrays.asList(3, 2, 1, 4), - new ArrayList<>(), Arrays.asList(3, 4)); - - - // adding and removing items at the same time - compareResult(Arrays.asList(1, 2, 3, 4), Arrays.asList(2, 3, 4, 5), - Arrays.asList(1), Arrays.asList(5)); - compareResult(Arrays.asList(1, 2, 3), Arrays.asList(4, 5, 6), - Arrays.asList(1, 2, 3), Arrays.asList(4, 5, 6)); - - // null cases - compareResult(Arrays.asList(1, 2, 3), null, Arrays.asList(1, 2, 3), new ArrayList<>()); - compareResult(null, Arrays.asList(3, 2, 1), new ArrayList<>(), Arrays.asList(1, 2, 3)); - compareResult(null, null, new ArrayList<>(), new ArrayList<>()); } private void assertEqualRoutes(Collection<RouteInfo> expected, Collection<RouteInfo> actual) { @@ -987,13 +962,6 @@ public class LinkPropertiesTest { assertEquals(expectedSet, actualSet); } - private <T> void compareResult(List<T> oldItems, List<T> newItems, List<T> expectRemoved, - List<T> expectAdded) { - CompareResult<T> result = new CompareResult<>(oldItems, newItems); - assertEquals(new ArraySet<>(expectAdded), new ArraySet<>(result.added)); - assertEquals(new ArraySet<>(expectRemoved), (new ArraySet<>(result.removed))); - } - private static LinkProperties makeLinkPropertiesForParceling() { LinkProperties source = new LinkProperties(); source.setInterfaceName(NAME); diff --git a/tests/net/integration/Android.bp b/tests/net/integration/Android.bp index 874bd4b97df1..69742b9bf7b2 100644 --- a/tests/net/integration/Android.bp +++ b/tests/net/integration/Android.bp @@ -32,6 +32,7 @@ android_test { "kotlin-reflect", "mockito-target-extended-minus-junit4", "net-tests-utils", + "service-connectivity", "services.core", "services.net", "testables", @@ -59,6 +60,7 @@ java_library { "net-tests-utils", ], libs: [ + "service-connectivity", "services.core", "services.net", ], diff --git a/tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt b/tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt index bc069e148b99..dba1856ea6d0 100644 --- a/tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt +++ b/tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt @@ -167,7 +167,7 @@ class ConnectivityServiceIntegrationTest { cm = ConnectivityManager(context, service) context.addMockSystemService(Context.CONNECTIVITY_SERVICE, cm) - service.systemReady() + service.systemReadyInternal() } private inner class TestConnectivityService(deps: Dependencies) : ConnectivityService( diff --git a/tests/net/jarjar-rules.txt b/tests/net/jarjar-rules.txt new file mode 100644 index 000000000000..ca8867206dda --- /dev/null +++ b/tests/net/jarjar-rules.txt @@ -0,0 +1,2 @@ +# Module library in frameworks/libs/net +rule com.android.net.module.util.** android.net.frameworktests.util.@1 diff --git a/tests/net/java/android/net/IpSecAlgorithmTest.java b/tests/net/java/android/net/IpSecAlgorithmTest.java index 8e9d08c705f3..2e1c29a2e405 100644 --- a/tests/net/java/android/net/IpSecAlgorithmTest.java +++ b/tests/net/java/android/net/IpSecAlgorithmTest.java @@ -16,34 +16,50 @@ package android.net; +import static android.net.IpSecAlgorithm.ALGO_TO_REQUIRED_FIRST_SDK; + +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import android.content.res.Resources; +import android.os.Build; import android.os.Parcel; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; +import com.android.internal.util.CollectionUtils; + import org.junit.Test; import org.junit.runner.RunWith; import java.util.AbstractMap.SimpleEntry; import java.util.Arrays; +import java.util.HashSet; import java.util.Map.Entry; import java.util.Random; +import java.util.Set; /** Unit tests for {@link IpSecAlgorithm}. */ @SmallTest @RunWith(AndroidJUnit4.class) public class IpSecAlgorithmTest { - private static final byte[] KEY_MATERIAL; + private final Resources mMockResources = mock(Resources.class); + static { KEY_MATERIAL = new byte[128]; new Random().nextBytes(KEY_MATERIAL); }; + private static byte[] generateKey(int keyLenInBits) { + return Arrays.copyOf(KEY_MATERIAL, keyLenInBits / 8); + } + @Test public void testNoTruncLen() throws Exception { Entry<String, Integer>[] authAndAeadList = @@ -53,7 +69,7 @@ public class IpSecAlgorithmTest { new SimpleEntry<>(IpSecAlgorithm.AUTH_HMAC_SHA256, 256), new SimpleEntry<>(IpSecAlgorithm.AUTH_HMAC_SHA384, 384), new SimpleEntry<>(IpSecAlgorithm.AUTH_HMAC_SHA512, 512), - new SimpleEntry<>(IpSecAlgorithm.AUTH_CRYPT_AES_GCM, 224) + new SimpleEntry<>(IpSecAlgorithm.AUTH_CRYPT_AES_GCM, 224), }; // Expect auth and aead algorithms to throw errors if trunclen is omitted. @@ -70,6 +86,52 @@ public class IpSecAlgorithmTest { new IpSecAlgorithm(IpSecAlgorithm.CRYPT_AES_CBC, Arrays.copyOf(KEY_MATERIAL, 256 / 8)); } + private void checkAuthKeyAndTruncLenValidation(String algoName, int keyLen, int truncLen) + throws Exception { + new IpSecAlgorithm(algoName, generateKey(keyLen), truncLen); + + try { + new IpSecAlgorithm(algoName, generateKey(keyLen)); + fail("Expected exception on unprovided auth trunclen"); + } catch (IllegalArgumentException pass) { + } + + try { + new IpSecAlgorithm(algoName, generateKey(keyLen + 8), truncLen); + fail("Invalid key length not validated"); + } catch (IllegalArgumentException pass) { + } + + try { + new IpSecAlgorithm(algoName, generateKey(keyLen), truncLen + 1); + fail("Invalid truncation length not validated"); + } catch (IllegalArgumentException pass) { + } + } + + private void checkCryptKeyLenValidation(String algoName, int keyLen) throws Exception { + new IpSecAlgorithm(algoName, generateKey(keyLen)); + + try { + new IpSecAlgorithm(algoName, generateKey(keyLen + 8)); + fail("Invalid key length not validated"); + } catch (IllegalArgumentException pass) { + } + } + + @Test + public void testValidationForAlgosAddedInS() throws Exception { + if (Build.VERSION.FIRST_SDK_INT <= Build.VERSION_CODES.R) { + return; + } + + for (int len : new int[] {160, 224, 288}) { + checkCryptKeyLenValidation(IpSecAlgorithm.CRYPT_AES_CTR, len); + } + checkAuthKeyAndTruncLenValidation(IpSecAlgorithm.AUTH_AES_XCBC, 128, 96); + checkAuthKeyAndTruncLenValidation(IpSecAlgorithm.AUTH_CRYPT_CHACHA20_POLY1305, 288, 128); + } + @Test public void testTruncLenValidation() throws Exception { for (int truncLen : new int[] {256, 512}) { @@ -127,4 +189,37 @@ public class IpSecAlgorithmTest { assertTrue("Parcel/Unparcel failed!", IpSecAlgorithm.equals(init, fin)); p.recycle(); } + + private static Set<String> getMandatoryAlgos() { + return CollectionUtils.filter( + ALGO_TO_REQUIRED_FIRST_SDK.keySet(), + i -> Build.VERSION.FIRST_SDK_INT >= ALGO_TO_REQUIRED_FIRST_SDK.get(i)); + } + + private static Set<String> getOptionalAlgos() { + return CollectionUtils.filter( + ALGO_TO_REQUIRED_FIRST_SDK.keySet(), + i -> Build.VERSION.FIRST_SDK_INT < ALGO_TO_REQUIRED_FIRST_SDK.get(i)); + } + + @Test + public void testGetSupportedAlgorithms() throws Exception { + assertTrue(IpSecAlgorithm.getSupportedAlgorithms().containsAll(getMandatoryAlgos())); + assertTrue(ALGO_TO_REQUIRED_FIRST_SDK.keySet().containsAll( + IpSecAlgorithm.getSupportedAlgorithms())); + } + + @Test + public void testLoadAlgos() throws Exception { + final Set<String> optionalAlgoSet = getOptionalAlgos(); + final String[] optionalAlgos = optionalAlgoSet.toArray(new String[0]); + + doReturn(optionalAlgos).when(mMockResources) + .getStringArray(com.android.internal.R.array.config_optionalIpSecAlgorithms); + + final Set<String> enabledAlgos = new HashSet<>(IpSecAlgorithm.loadAlgos(mMockResources)); + final Set<String> expectedAlgos = ALGO_TO_REQUIRED_FIRST_SDK.keySet(); + + assertEquals(expectedAlgos, enabledAlgos); + } } diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java index 7dfac9c8c357..1aae6cb35239 100644 --- a/tests/net/java/com/android/server/ConnectivityServiceTest.java +++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java @@ -313,6 +313,8 @@ public class ConnectivityServiceTest { private static final long TIMESTAMP = 1234L; private static final int NET_ID = 110; + // Set a non-zero value to verify the flow to set tcp init rwnd value. + private static final int TEST_TCP_INIT_RWND = 60; private static final String CLAT_PREFIX = "v4-"; private static final String MOBILE_IFNAME = "test_rmnet_data0"; @@ -355,6 +357,7 @@ public class ConnectivityServiceTest { @Mock LocationManager mLocationManager; @Mock AppOpsManager mAppOpsManager; @Mock TelephonyManager mTelephonyManager; + @Mock MockableSystemProperties mSystemProperties; private ArgumentCaptor<ResolverParamsParcel> mResolverParamsParcelCaptor = ArgumentCaptor.forClass(ResolverParamsParcel.class); @@ -1246,7 +1249,7 @@ public class ConnectivityServiceTest { // Create local CM before sending system ready so that we can answer // getSystemService() correctly. mCm = new WrappedConnectivityManager(InstrumentationRegistry.getContext(), mService); - mService.systemReady(); + mService.systemReadyInternal(); mockVpn(Process.myUid()); mCm.bindProcessToNetwork(null); @@ -1257,15 +1260,15 @@ public class ConnectivityServiceTest { } private ConnectivityService.Dependencies makeDependencies() { - final MockableSystemProperties systemProperties = spy(new MockableSystemProperties()); - when(systemProperties.getInt("net.tcp.default_init_rwnd", 0)).thenReturn(0); - when(systemProperties.getBoolean("ro.radio.noril", false)).thenReturn(false); - + doReturn(TEST_TCP_INIT_RWND).when(mSystemProperties) + .getInt("net.tcp.default_init_rwnd", 0); + doReturn(false).when(mSystemProperties).getBoolean("ro.radio.noril", false); + doNothing().when(mSystemProperties).setTcpInitRwnd(anyInt()); final ConnectivityService.Dependencies deps = mock(ConnectivityService.Dependencies.class); doReturn(mCsHandlerThread).when(deps).makeHandlerThread(); doReturn(new TestNetIdManager()).when(deps).makeNetIdManager(); doReturn(mNetworkStack).when(deps).getNetworkStack(); - doReturn(systemProperties).when(deps).getSystemProperties(); + doReturn(mSystemProperties).when(deps).getSystemProperties(); doReturn(mock(ProxyTracker.class)).when(deps).makeProxyTracker(any(), any()); doReturn(mMetricsService).when(deps).getMetricsLogger(); doReturn(true).when(deps).queryUserAccess(anyInt(), anyInt()); @@ -6146,7 +6149,7 @@ public class ConnectivityServiceTest { // Switching default network updates TCP buffer sizes. verifyTcpBufferSizeChange(ConnectivityService.DEFAULT_TCP_BUFFER_SIZES); - + verify(mSystemProperties, times(1)).setTcpInitRwnd(eq(TEST_TCP_INIT_RWND)); // Add an IPv4 address. Expect prefix discovery to be stopped. Netd doesn't tell us that // the NAT64 prefix was removed because one was never discovered. cellLp.addLinkAddress(myIpv4); @@ -6583,14 +6586,14 @@ public class ConnectivityServiceTest { mCellNetworkAgent.connect(false); networkCallback.expectAvailableCallbacksUnvalidated(mCellNetworkAgent); verifyTcpBufferSizeChange(ConnectivityService.DEFAULT_TCP_BUFFER_SIZES); - + verify(mSystemProperties, times(1)).setTcpInitRwnd(eq(TEST_TCP_INIT_RWND)); // Change link Properties should have updated tcp buffer size. LinkProperties lp = new LinkProperties(); lp.setTcpBufferSizes(testTcpBufferSizes); mCellNetworkAgent.sendLinkProperties(lp); networkCallback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED, mCellNetworkAgent); verifyTcpBufferSizeChange(testTcpBufferSizes); - + verify(mSystemProperties, times(2)).setTcpInitRwnd(eq(TEST_TCP_INIT_RWND)); // Clean up. mCellNetworkAgent.disconnect(); networkCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent); diff --git a/tests/net/java/com/android/server/connectivity/DnsManagerTest.java b/tests/net/java/com/android/server/connectivity/DnsManagerTest.java index 753dbf80b449..f5b85ca06f92 100644 --- a/tests/net/java/com/android/server/connectivity/DnsManagerTest.java +++ b/tests/net/java/com/android/server/connectivity/DnsManagerTest.java @@ -98,7 +98,6 @@ public class DnsManagerTest { @Mock Context mCtx; @Mock IDnsResolver mMockDnsResolver; - @Mock MockableSystemProperties mSystemProperties; private void assertResolverOptionsEquals( @NonNull ResolverOptionsParcel actual, @@ -137,7 +136,7 @@ public class DnsManagerTest { mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider()); when(mCtx.getContentResolver()).thenReturn(mContentResolver); - mDnsManager = new DnsManager(mCtx, mMockDnsResolver, mSystemProperties); + mDnsManager = new DnsManager(mCtx, mMockDnsResolver); // Clear the private DNS settings Settings.Global.putString(mContentResolver, PRIVATE_DNS_DEFAULT_MODE, ""); @@ -159,7 +158,6 @@ public class DnsManagerTest { // Send a validation event that is tracked on the alternate netId mDnsManager.updateTransportsForNetwork(TEST_NETID, TEST_TRANSPORT_TYPES); mDnsManager.noteDnsServersForNetwork(TEST_NETID, lp); - mDnsManager.setDefaultDnsSystemProperties(lp.getDnsServers()); mDnsManager.flushVmDnsCache(); mDnsManager.updateTransportsForNetwork(TEST_NETID_ALTERNATE, TEST_TRANSPORT_TYPES); mDnsManager.noteDnsServersForNetwork(TEST_NETID_ALTERNATE, lp); @@ -196,7 +194,6 @@ public class DnsManagerTest { })); mDnsManager.updateTransportsForNetwork(TEST_NETID, TEST_TRANSPORT_TYPES); mDnsManager.noteDnsServersForNetwork(TEST_NETID, lp); - mDnsManager.setDefaultDnsSystemProperties(lp.getDnsServers()); mDnsManager.flushVmDnsCache(); fixedLp = new LinkProperties(lp); mDnsManager.updatePrivateDnsStatus(TEST_NETID, fixedLp); @@ -232,7 +229,6 @@ public class DnsManagerTest { lp.addDnsServer(InetAddress.getByName("3.3.3.3")); mDnsManager.updateTransportsForNetwork(TEST_NETID, TEST_TRANSPORT_TYPES); mDnsManager.noteDnsServersForNetwork(TEST_NETID, lp); - mDnsManager.setDefaultDnsSystemProperties(lp.getDnsServers()); mDnsManager.flushVmDnsCache(); mDnsManager.updatePrivateDnsValidation( new DnsManager.PrivateDnsValidationUpdate(TEST_NETID, @@ -246,7 +242,6 @@ public class DnsManagerTest { mDnsManager.getPrivateDnsConfig()); mDnsManager.updateTransportsForNetwork(TEST_NETID, TEST_TRANSPORT_TYPES); mDnsManager.noteDnsServersForNetwork(TEST_NETID, lp); - mDnsManager.setDefaultDnsSystemProperties(lp.getDnsServers()); mDnsManager.flushVmDnsCache(); mDnsManager.updatePrivateDnsValidation( new DnsManager.PrivateDnsValidationUpdate(TEST_NETID_UNTRACKED, @@ -295,7 +290,6 @@ public class DnsManagerTest { mDnsManager.getPrivateDnsConfig()); mDnsManager.updateTransportsForNetwork(TEST_NETID, TEST_TRANSPORT_TYPES); mDnsManager.noteDnsServersForNetwork(TEST_NETID, lp); - mDnsManager.setDefaultDnsSystemProperties(lp.getDnsServers()); mDnsManager.flushVmDnsCache(); mDnsManager.updatePrivateDnsValidation( new DnsManager.PrivateDnsValidationUpdate(TEST_NETID, @@ -341,7 +335,6 @@ public class DnsManagerTest { lp.addDnsServer(InetAddress.getByName("4.4.4.4")); mDnsManager.updateTransportsForNetwork(TEST_NETID, TEST_TRANSPORT_TYPES); mDnsManager.noteDnsServersForNetwork(TEST_NETID, lp); - mDnsManager.setDefaultDnsSystemProperties(lp.getDnsServers()); mDnsManager.flushVmDnsCache(); final ArgumentCaptor<ResolverParamsParcel> resolverParamsParcelCaptor = diff --git a/wifi/api/current.txt b/wifi/api/current.txt index 2d48a8325d6e..f0cf75c8bf72 100644 --- a/wifi/api/current.txt +++ b/wifi/api/current.txt @@ -563,6 +563,7 @@ package android.net.wifi.aware { method public int getMaxServiceNameLength(); method public int getMaxServiceSpecificInfoLength(); method public int getSupportedCipherSuites(); + method public boolean isInstantCommunicationModeSupported(); method public void writeToParcel(android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.aware.Characteristics> CREATOR; field public static final int WIFI_AWARE_CIPHER_SUITE_NCS_SK_128 = 1; // 0x1 @@ -584,7 +585,7 @@ package android.net.wifi.aware { method public void onPublishStarted(@NonNull android.net.wifi.aware.PublishDiscoverySession); method public void onServiceDiscovered(android.net.wifi.aware.PeerHandle, byte[], java.util.List<byte[]>); method public void onServiceDiscoveredWithinRange(android.net.wifi.aware.PeerHandle, byte[], java.util.List<byte[]>, int); - method public void onServiceLost(@NonNull android.net.wifi.aware.PeerHandle); + method public void onServiceLost(@NonNull android.net.wifi.aware.PeerHandle, int); method public void onSessionConfigFailed(); method public void onSessionConfigUpdated(); method public void onSessionTerminated(); @@ -661,9 +662,12 @@ package android.net.wifi.aware { method public android.net.wifi.aware.Characteristics getCharacteristics(); method public boolean isAvailable(); method public boolean isDeviceAttached(); + method public boolean isInstantCommunicationModeEnabled(); field public static final String ACTION_WIFI_AWARE_STATE_CHANGED = "android.net.wifi.aware.action.WIFI_AWARE_STATE_CHANGED"; field public static final int WIFI_AWARE_DATA_PATH_ROLE_INITIATOR = 0; // 0x0 field public static final int WIFI_AWARE_DATA_PATH_ROLE_RESPONDER = 1; // 0x1 + field public static final int WIFI_AWARE_DISCOVERY_LOST_REASON_PEER_NOT_VISIBLE = 1; // 0x1 + field public static final int WIFI_AWARE_DISCOVERY_LOST_REASON_UNKNOWN = 0; // 0x0 } public final class WifiAwareNetworkInfo implements android.os.Parcelable android.net.TransportInfo { @@ -803,13 +807,13 @@ package android.net.wifi.hotspot2.pps { method public String getFriendlyName(); method @Nullable public long[] getMatchAllOis(); method @Nullable public long[] getMatchAnyOis(); - method @Nullable public String[] getOtherHomePartners(); + method @NonNull public java.util.Collection<java.lang.String> getOtherHomePartnersList(); method public long[] getRoamingConsortiumOis(); method public void setFqdn(String); method public void setFriendlyName(String); method public void setMatchAllOis(@Nullable long[]); method public void setMatchAnyOis(@Nullable long[]); - method public void setOtherHomePartners(@Nullable String[]); + method public void setOtherHomePartnersList(@NonNull java.util.Collection<java.lang.String>); method public void setRoamingConsortiumOis(long[]); method public void writeToParcel(android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.hotspot2.pps.HomeSp> CREATOR; diff --git a/wifi/api/system-current.txt b/wifi/api/system-current.txt index 98cb849084d4..754a8dc408a4 100644 --- a/wifi/api/system-current.txt +++ b/wifi/api/system-current.txt @@ -800,6 +800,10 @@ package android.net.wifi.aware { method @Deprecated public android.net.NetworkSpecifier createNetworkSpecifierPmk(@NonNull android.net.wifi.aware.PeerHandle, @NonNull byte[]); } + public class WifiAwareManager { + method public void enableInstantCommunicationMode(boolean); + } + public class WifiAwareSession implements java.lang.AutoCloseable { method public android.net.NetworkSpecifier createNetworkSpecifierPmk(int, @NonNull byte[], @NonNull byte[]); } diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java index 7c2556d8cffd..fd4e1ddac3a2 100644 --- a/wifi/java/android/net/wifi/WifiConfiguration.java +++ b/wifi/java/android/net/wifi/WifiConfiguration.java @@ -1626,6 +1626,14 @@ public class WifiConfiguration implements Parcelable { private boolean mHasEverConnected; /** + * Boolean indicating if captive portal has never been detected on this network. + * + * This should be true by default, for newly created WifiConfigurations until a captive + * portal is detected. + */ + private boolean mHasNeverDetectedCaptivePortal = true; + + /** * set whether this network is visible in latest Qualified Network Selection * @param seen value set to candidate * @hide @@ -1714,6 +1722,19 @@ public class WifiConfiguration implements Parcelable { return mHasEverConnected; } + /** + * Set whether a captive portal has never been detected on this network. + * @hide + */ + public void setHasNeverDetectedCaptivePortal(boolean value) { + mHasNeverDetectedCaptivePortal = value; + } + + /** @hide */ + public boolean hasNeverDetectedCaptivePortal() { + return mHasNeverDetectedCaptivePortal; + } + /** @hide */ public NetworkSelectionStatus() { // previously stored configs will not have this parameter, so we default to false. @@ -1989,6 +2010,7 @@ public class WifiConfiguration implements Parcelable { setCandidateScore(source.getCandidateScore()); setConnectChoice(source.getConnectChoice()); setHasEverConnected(source.hasEverConnected()); + setHasNeverDetectedCaptivePortal(source.hasNeverDetectedCaptivePortal()); } /** @hide */ @@ -2008,6 +2030,7 @@ public class WifiConfiguration implements Parcelable { dest.writeInt(CONNECT_CHOICE_NOT_EXISTS); } dest.writeInt(hasEverConnected() ? 1 : 0); + dest.writeInt(hasNeverDetectedCaptivePortal() ? 1 : 0); } /** @hide */ @@ -2026,6 +2049,7 @@ public class WifiConfiguration implements Parcelable { setConnectChoice(null); } setHasEverConnected(in.readInt() != 0); + setHasNeverDetectedCaptivePortal(in.readInt() != 0); } } @@ -2287,6 +2311,8 @@ public class WifiConfiguration implements Parcelable { } sbuf.append(" hasEverConnected: ") .append(mNetworkSelectionStatus.hasEverConnected()).append("\n"); + sbuf.append(" hasNeverDetectedCaptivePortal: ") + .append(mNetworkSelectionStatus.hasNeverDetectedCaptivePortal()).append("\n"); if (this.numAssociation > 0) { sbuf.append(" numAssociation ").append(this.numAssociation).append("\n"); diff --git a/wifi/java/android/net/wifi/aware/Characteristics.java b/wifi/java/android/net/wifi/aware/Characteristics.java index d5fd48e9e7b3..116786516b3b 100644 --- a/wifi/java/android/net/wifi/aware/Characteristics.java +++ b/wifi/java/android/net/wifi/aware/Characteristics.java @@ -17,6 +17,7 @@ package android.net.wifi.aware; import android.annotation.IntDef; +import android.net.wifi.util.SdkLevelUtil; import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; @@ -37,6 +38,9 @@ public final class Characteristics implements Parcelable { public static final String KEY_MAX_MATCH_FILTER_LENGTH = "key_max_match_filter_length"; /** @hide */ public static final String KEY_SUPPORTED_CIPHER_SUITES = "key_supported_cipher_suites"; + /** @hide */ + public static final String KEY_IS_INSTANT_COMMUNICATION_MODE_SUPPORTED = + "key_is_instant_communication_mode_supported"; private Bundle mCharacteristics = new Bundle(); @@ -83,6 +87,17 @@ public final class Characteristics implements Parcelable { return mCharacteristics.getInt(KEY_MAX_MATCH_FILTER_LENGTH); } + /** + * Check if instant communication mode is supported by device. + * @return True if supported, false otherwise. + */ + public boolean isInstantCommunicationModeSupported() { + if (!SdkLevelUtil.isAtLeastS()) { + throw new UnsupportedOperationException(); + } + return mCharacteristics.getBoolean(KEY_IS_INSTANT_COMMUNICATION_MODE_SUPPORTED); + } + /** @hide */ @IntDef(flag = true, prefix = { "WIFI_AWARE_CIPHER_SUITE_" }, value = { WIFI_AWARE_CIPHER_SUITE_NCS_SK_128, diff --git a/wifi/java/android/net/wifi/aware/DiscoverySessionCallback.java b/wifi/java/android/net/wifi/aware/DiscoverySessionCallback.java index e3800ad6ef36..da8e17e2f41a 100644 --- a/wifi/java/android/net/wifi/aware/DiscoverySessionCallback.java +++ b/wifi/java/android/net/wifi/aware/DiscoverySessionCallback.java @@ -191,14 +191,18 @@ public class DiscoverySessionCallback { } /** - * Called when the discovered peer is no longer visible. All further operations on this - * discovery session will fail. If the peer is visible again, + * Called when the discovered service is not available. All further operations on this + * discovery session will fail. If the service is available again, * {@link #onServiceDiscovered(PeerHandle, byte[], List)} or * {@link #onServiceDiscoveredWithinRange(PeerHandle, byte[], List, int)} will be called. * * @param peerHandle An opaque handle to the peer matching our discovery operation. + * @param reason Discovered service lost reason code. One of + * {@link WifiAwareManager#WIFI_AWARE_DISCOVERY_LOST_REASON_PEER_NOT_VISIBLE}, + * {@link WifiAwareManager#WIFI_AWARE_DISCOVERY_LOST_REASON_UNKNOWN */ - public void onServiceLost(@NonNull PeerHandle peerHandle) { + public void onServiceLost(@NonNull PeerHandle peerHandle, + @WifiAwareManager.DiscoveryLostReasonCode int reason) { /* empty */ } } diff --git a/wifi/java/android/net/wifi/aware/IWifiAwareManager.aidl b/wifi/java/android/net/wifi/aware/IWifiAwareManager.aidl index f5b1edce1d69..cd2ca692137d 100644 --- a/wifi/java/android/net/wifi/aware/IWifiAwareManager.aidl +++ b/wifi/java/android/net/wifi/aware/IWifiAwareManager.aidl @@ -37,6 +37,8 @@ interface IWifiAwareManager boolean isUsageEnabled(); Characteristics getCharacteristics(); boolean isDeviceAttached(); + void enableInstantCommunicationMode(in String callingPackage, boolean enable); + boolean isInstantCommunicationModeEnabled(); // client API void connect(in IBinder binder, in String callingPackage, in String callingFeatureId, diff --git a/wifi/java/android/net/wifi/aware/WifiAwareManager.java b/wifi/java/android/net/wifi/aware/WifiAwareManager.java index d6e46fd52caf..bb146e37d48c 100644 --- a/wifi/java/android/net/wifi/aware/WifiAwareManager.java +++ b/wifi/java/android/net/wifi/aware/WifiAwareManager.java @@ -22,12 +22,14 @@ 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.content.Context; import android.net.ConnectivityManager; import android.net.NetworkRequest; import android.net.NetworkSpecifier; import android.net.wifi.util.HexEncoding; +import android.net.wifi.util.SdkLevelUtil; import android.os.Binder; import android.os.Build; import android.os.Bundle; @@ -151,6 +153,27 @@ public class WifiAwareManager { */ public static final int WIFI_AWARE_DATA_PATH_ROLE_RESPONDER = 1; + /** @hide */ + @IntDef({ + WIFI_AWARE_DISCOVERY_LOST_REASON_UNKNOWN, + WIFI_AWARE_DISCOVERY_LOST_REASON_PEER_NOT_VISIBLE}) + @Retention(RetentionPolicy.SOURCE) + public @interface DiscoveryLostReasonCode { + } + + /** + * Reason code provided in {@link DiscoverySessionCallback#onServiceLost(PeerHandle, int)} + * indicating that the service was lost for unknown reason. + */ + public static final int WIFI_AWARE_DISCOVERY_LOST_REASON_UNKNOWN = 0; + + /** + * Reason code provided in {@link DiscoverySessionCallback#onServiceLost(PeerHandle, int)} + * indicating that the service advertised by the peer is no longer visible. This may be because + * the peer is out of range or because the peer stopped advertising this service. + */ + public static final int WIFI_AWARE_DISCOVERY_LOST_REASON_PEER_NOT_VISIBLE = 1; + private final Context mContext; private final IWifiAwareManager mService; @@ -187,6 +210,9 @@ public class WifiAwareManager { * or not (false). */ public boolean isDeviceAttached() { + if (!SdkLevelUtil.isAtLeastS()) { + throw new UnsupportedOperationException(); + } try { return mService.isDeviceAttached(); } catch (RemoteException e) { @@ -195,6 +221,42 @@ public class WifiAwareManager { } /** + * Enable the Wifi Aware Instant communication mode. If the device doesn't support this feature + * calling this API will result no action. + * @see Characteristics#isInstantCommunicationModeSupported() + * @param enable true for enable, false otherwise. + * @hide + */ + @SystemApi + public void enableInstantCommunicationMode(boolean enable) { + if (!SdkLevelUtil.isAtLeastS()) { + throw new UnsupportedOperationException(); + } + try { + mService.enableInstantCommunicationMode(mContext.getOpPackageName(), enable); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Return the current status of the Wifi Aware instant communication mode. + * If the device doesn't support this feature, return will always be false. + * @see Characteristics#isInstantCommunicationModeSupported() + * @return true if it is enabled, false otherwise. + */ + public boolean isInstantCommunicationModeEnabled() { + if (!SdkLevelUtil.isAtLeastS()) { + throw new UnsupportedOperationException(); + } + try { + return mService.isInstantCommunicationModeEnabled(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** * Returns the characteristics of the Wi-Fi Aware interface: a set of parameters which specify * limitations on configurations, e.g. the maximum service name length. * @@ -695,7 +757,8 @@ public class WifiAwareManager { break; case CALLBACK_MATCH_EXPIRED: mOriginalCallback - .onServiceLost(new PeerHandle(msg.arg1)); + .onServiceLost(new PeerHandle(msg.arg1), + WIFI_AWARE_DISCOVERY_LOST_REASON_PEER_NOT_VISIBLE); } } }; diff --git a/wifi/java/android/net/wifi/hotspot2/pps/HomeSp.java b/wifi/java/android/net/wifi/hotspot2/pps/HomeSp.java index 35a8ff6095e0..64aad613c8b2 100644 --- a/wifi/java/android/net/wifi/hotspot2/pps/HomeSp.java +++ b/wifi/java/android/net/wifi/hotspot2/pps/HomeSp.java @@ -16,6 +16,7 @@ package android.net.wifi.hotspot2.pps; +import android.annotation.NonNull; import android.annotation.Nullable; import android.os.Parcel; import android.os.Parcelable; @@ -24,6 +25,7 @@ import android.util.Log; import java.nio.charset.StandardCharsets; import java.util.Arrays; +import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Map; @@ -214,23 +216,52 @@ public final class HomeSp implements Parcelable { * * @param otherHomePartners Array of Strings containing the FQDNs of other Home partner * providers + * @hide */ public void setOtherHomePartners(@Nullable String[] otherHomePartners) { mOtherHomePartners = otherHomePartners; } /** + * Set the list of FQDN (Fully Qualified Domain Name) of other Home partner providers. + * + * @param otherHomePartners Collection of Strings containing the FQDNs of other Home partner + * providers + */ + public void setOtherHomePartnersList(@NonNull Collection<String> otherHomePartners) { + if (otherHomePartners == null) { + return; + } + mOtherHomePartners = otherHomePartners.toArray(new String[otherHomePartners.size()]); + } + + /** * Get the list of FQDN (Fully Qualified Domain Name) of other Home partner providers set in * the profile. * * @return Array of Strings containing the FQDNs of other Home partner providers set in the * profile + * @hide */ public @Nullable String[] getOtherHomePartners() { return mOtherHomePartners; } /** + * Get the list of FQDN (Fully Qualified Domain Name) of other Home partner providers set in + * the profile. + * + * @return Collection of Strings containing the FQDNs of other Home partner providers set in the + * profile + */ + public @NonNull Collection<String> getOtherHomePartnersList() { + if (mOtherHomePartners == null) { + return Collections.emptyList(); + } + return Arrays.asList(mOtherHomePartners); + } + + /** * List of Organization Identifiers (OIs) identifying a roaming consortium of * which this provider is a member. */ diff --git a/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java b/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java index 5fe0cb42073d..2cf7f2c1b8cf 100644 --- a/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java +++ b/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java @@ -16,11 +16,13 @@ package android.net.wifi.aware; +import static android.net.wifi.aware.WifiAwareManager.WIFI_AWARE_DISCOVERY_LOST_REASON_PEER_NOT_VISIBLE; import static android.net.wifi.aware.WifiAwareNetworkSpecifier.NETWORK_SPECIFIER_TYPE_IB; import static org.hamcrest.core.IsEqual.equalTo; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; +import static org.junit.Assume.assumeTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; @@ -37,6 +39,7 @@ import android.content.pm.PackageManager; import android.net.MacAddress; import android.net.wifi.RttManager; import android.net.wifi.util.HexEncoding; +import android.net.wifi.util.SdkLevelUtil; import android.os.Build; import android.os.Handler; import android.os.IBinder; @@ -155,6 +158,19 @@ public class WifiAwareManagerTest { verify(mockAwareService).isDeviceAttached(); } + /** + * Validate pass-through of isInstantCommunicationModeEnabled() and + * enableInstantCommunicationMode() API + */ + @Test + public void testEnableInstantCommunicationMode() throws Exception { + assumeTrue(SdkLevelUtil.isAtLeastS()); + mDut.isInstantCommunicationModeEnabled(); + verify(mockAwareService).isInstantCommunicationModeEnabled(); + mDut.enableInstantCommunicationMode(true); + verify(mockAwareService).enableInstantCommunicationMode(anyString(), eq(true)); + } + /* * WifiAwareEventCallbackProxy Tests */ @@ -372,7 +388,8 @@ public class WifiAwareManagerTest { // (5) discovery session is no longer visible sessionProxyCallback.getValue().onMatchExpired(peerHandle.peerId); mMockLooper.dispatchAll(); - inOrder.verify(mockSessionCallback).onServiceLost(peerIdCaptor.capture()); + inOrder.verify(mockSessionCallback).onServiceLost(peerIdCaptor.capture(), + eq(WIFI_AWARE_DISCOVERY_LOST_REASON_PEER_NOT_VISIBLE)); assertEquals(peerHandle.peerId, peerIdCaptor.getValue().peerId); // (6) terminate @@ -520,7 +537,8 @@ public class WifiAwareManagerTest { // (5) discovery session is no longer visible sessionProxyCallback.getValue().onMatchExpired(peerHandle.peerId); mMockLooper.dispatchAll(); - inOrder.verify(mockSessionCallback).onServiceLost(peerIdCaptor.capture()); + inOrder.verify(mockSessionCallback).onServiceLost(peerIdCaptor.capture(), + eq(WIFI_AWARE_DISCOVERY_LOST_REASON_PEER_NOT_VISIBLE)); assertEquals(peerHandle.peerId, peerIdCaptor.getValue().peerId); // (6) terminate diff --git a/wifi/tests/src/android/net/wifi/hotspot2/pps/HomeSpTest.java b/wifi/tests/src/android/net/wifi/hotspot2/pps/HomeSpTest.java index 93d471ab6b81..fe889fc31ed4 100644 --- a/wifi/tests/src/android/net/wifi/hotspot2/pps/HomeSpTest.java +++ b/wifi/tests/src/android/net/wifi/hotspot2/pps/HomeSpTest.java @@ -16,6 +16,7 @@ package android.net.wifi.hotspot2.pps; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; @@ -26,7 +27,9 @@ import androidx.test.filters.SmallTest; import org.junit.Test; import java.nio.charset.StandardCharsets; +import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.HashMap; import java.util.Map; @@ -35,6 +38,7 @@ import java.util.Map; */ @SmallTest public class HomeSpTest { + private static final String[] OTHER_HOME_PARTNER_LIST = new String[]{"partner1", "partner2"}; /** * Helper function for creating a map of home network IDs for testing. @@ -62,7 +66,7 @@ public class HomeSpTest { homeSp.setHomeNetworkIds(homeNetworkIds); homeSp.setMatchAllOis(new long[] {0x11L, 0x22L}); homeSp.setMatchAnyOis(new long[] {0x33L, 0x44L}); - homeSp.setOtherHomePartners(new String[] {"partner1", "partner2"}); + homeSp.setOtherHomePartners(OTHER_HOME_PARTNER_LIST); homeSp.setRoamingConsortiumOis(new long[] {0x55, 0x66}); return homeSp; } @@ -218,4 +222,34 @@ public class HomeSpTest { HomeSp copySp = new HomeSp(sourceSp); assertTrue(copySp.equals(sourceSp)); } + + /** + * Verify that the getOtherHomePartnersList gets the list of partners as expected. + * + * @throws Exception + */ + @Test + public void validateGetOtherHomePartnersList() throws Exception { + HomeSp homeSp = createHomeSpWithoutHomeNetworkIds(); + + Collection<String> otherHomePartnersList = homeSp.getOtherHomePartnersList(); + assertEquals(2, otherHomePartnersList.size()); + assertTrue(Arrays.equals(OTHER_HOME_PARTNER_LIST, otherHomePartnersList.toArray())); + } + + /** + * Verify that the setOtherHomePartnersList sets the list of partners as expected. + * + * @throws Exception + */ + @Test + public void validateSetOtherHomePartnersList() throws Exception { + HomeSp homeSp = createHomeSpWithoutHomeNetworkIds(); + + final Collection<String> homePartners = + new ArrayList<>(Arrays.asList(OTHER_HOME_PARTNER_LIST)); + + homeSp.setOtherHomePartnersList(homePartners); + assertTrue(Arrays.equals(homeSp.getOtherHomePartners(), OTHER_HOME_PARTNER_LIST)); + } } |